blob: 9c35738a95cf89162720289e6e15d343f1918861 [file] [log] [blame]
/* Next Runtime (ABI-2) private.
Copyright (C) 2011-2021 Free Software Foundation, Inc.
Contributed by Iain Sandoe and based, in part, on an implementation in
'branches/apple/trunk' contributed by Apple Computer Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* The NeXT ABI2 is used for m64 implementations on Darwin/OSX machines.
This version is intended to match (logically) the output of Apple's
4.2.1 compiler. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "stringpool.h"
#include "attribs.h"
#ifdef OBJCPLUS
#include "cp/cp-tree.h"
#else
#include "c/c-tree.h"
#include "c/c-lang.h"
#endif
#include "langhooks.h"
#include "c-family/c-objc.h"
#include "objc-act.h"
/* When building Objective-C++, we are not linking against the C front-end
and so need to replicate the C tree-construction functions in some way. */
#ifdef OBJCPLUS
#define OBJCP_REMAP_FUNCTIONS
#include "objcp-decl.h"
#endif /* OBJCPLUS */
#include "target.h"
#include "tree-iterator.h"
#include "objc-runtime-hooks.h"
#include "objc-runtime-shared-support.h"
#include "objc-encoding.h"
/* ABI 2 Private definitions. */
#define DEF_CONSTANT_STRING_CLASS_NAME "NSConstantString"
#define TAG_GETCLASS "objc_getClass"
#define TAG_GETMETACLASS "objc_getMetaClass"
#define TAG_MSGSEND "objc_msgSend"
#define TAG_MSGSENDID "objc_msgSendId"
#define TAG_MSGSENDSUPER "objc_msgSendSuper2"
#define TAG_MSGSEND_STRET "objc_msgSend_stret"
#define TAG_MSGSENDID_STRET "objc_msgSendId_stret"
#define TAG_MSGSENDSUPER_STRET "objc_msgSendSuper2_stret"
#define USE_FIXUP_BEFORE 100600
#define TAG_FIXUP "_fixup"
#define TAG_NEXT_EHVTABLE_NAME "objc_ehtype_vtable"
#define TAG_V2_EH_TYPE "objc_ehtype_t"
#define UTAG_V2_CLASS "_class_t"
#define UTAG_V2_CLASS_RO "_class_ro_t"
#define UTAG_V2_PROTOCOL "_protocol_t"
#define UTAG_V2_PROTOCOL_LIST "_protocol_list_t"
#define UTAG_V2_EH_TYPE "_objc_ehtype_t"
#define OBJC2_CLS_HAS_CXX_STRUCTORS 0x0004L
enum objc_v2_tree_index
{
/* Templates. */
OCTI_V2_CLS_TEMPL,
OCTI_V2_CAT_TEMPL,
OCTI_V2_CLS_RO_TEMPL,
OCTI_V2_PROTO_TEMPL,
OCTI_V2_IVAR_TEMPL,
OCTI_V2_IVAR_LIST_TEMPL,
OCTI_V2_MESSAGE_REF_TEMPL,
OCTI_V2_SUPER_MESSAGE_REF_TEMPL,
OCTI_V2_MESSAGE_SELECTOR_TYPE,
OCTI_V2_SUPER_MESSAGE_SELECTOR_TYPE,
OCTI_V2_IMP_TYPE,
OCTI_V2_SUPER_IMP_TYPE,
OCTI_V2_CACHE_DECL,
OCTI_V2_VTABLE_DECL,
OCTI_V2_PROPERTY_TEMPL,
/* V2 messaging. */
OCTI_V2_UMSG_FIXUP_DECL,
OCTI_V2_UMSG_STRET_FIXUP_DECL,
OCTI_V2_UMSG_ID_FIXUP_DECL,
OCTI_V2_UMSG_ID_STRET_FIXUP_DECL,
OCTI_V2_UMSG_SUPER2_FIXUP_DECL,
OCTI_V2_UMSG_SUPER2_STRET_FIXUP_DECL,
/* Exceptions - related. */
OCTI_V2_BEGIN_CATCH_DECL,
OCTI_V2_END_CATCH_DECL,
OCTI_V2_RETHROW_DECL,
OCTI_V2_MAX
};
#define objc_v2_class_template objc_v2_global_trees[OCTI_V2_CLS_TEMPL]
#define objc_v2_class_ro_template \
objc_v2_global_trees[OCTI_V2_CLS_RO_TEMPL]
#define objc_v2_category_template \
objc_v2_global_trees[OCTI_V2_CAT_TEMPL]
#define objc_v2_protocol_template \
objc_v2_global_trees[OCTI_V2_PROTO_TEMPL]
/* struct message_ref_t */
#define objc_v2_message_ref_template \
objc_v2_global_trees[OCTI_V2_MESSAGE_REF_TEMPL]
#define objc_v2_ivar_list_ptr objc_v2_global_trees[OCTI_V2_IVAR_LIST_TEMPL]
/* struct super_message_ref_t */
#define objc_v2_super_message_ref_template \
objc_v2_global_trees[OCTI_V2_SUPER_MESSAGE_REF_TEMPL]
/* struct message_ref_t* */
#define objc_v2_selector_type objc_v2_global_trees[OCTI_V2_MESSAGE_SELECTOR_TYPE]
/* struct super_super_message_ref_t */
#define objc_v2_super_selector_type \
objc_v2_global_trees[OCTI_V2_SUPER_MESSAGE_SELECTOR_TYPE]
#define objc_v2_imp_type objc_v2_global_trees[OCTI_V2_IMP_TYPE]
#define objc_v2_super_imp_type objc_v2_global_trees[OCTI_V2_SUPER_IMP_TYPE]
#define UOBJC_V2_CACHE_decl objc_v2_global_trees[OCTI_V2_CACHE_DECL]
#define UOBJC_V2_VTABLE_decl objc_v2_global_trees[OCTI_V2_VTABLE_DECL]
#define objc_v2_ivar_template objc_v2_global_trees[OCTI_V2_IVAR_TEMPL]
#define objc_v2_property_template \
objc_v2_global_trees[OCTI_V2_PROPERTY_TEMPL]
/* V2 Messaging */
/* objc_msgSend_fixup_rtp */
#define umsg_fixup_decl objc_v2_global_trees[OCTI_V2_UMSG_FIXUP_DECL]
/* objc_msgSend_stret_fixup_rtp */
#define umsg_stret_fixup_decl objc_v2_global_trees[OCTI_V2_UMSG_STRET_FIXUP_DECL]
/* objc_msgSendId_fixup_rtp */
#define umsg_id_fixup_decl objc_v2_global_trees[OCTI_V2_UMSG_ID_FIXUP_DECL]
/* objc_msgSendId_stret_fixup_rtp */
#define umsg_id_stret_fixup_decl \
objc_v2_global_trees[OCTI_V2_UMSG_ID_STRET_FIXUP_DECL]
/* objc_msgSendSuper2_fixup_rtp */
#define umsg_id_super2_fixup_decl \
objc_v2_global_trees[OCTI_V2_UMSG_SUPER2_FIXUP_DECL]
/* objc_msgSendSuper2_stret_fixup_rtp */
#define umsg_id_super2_stret_fixup_decl \
objc_v2_global_trees[OCTI_V2_UMSG_SUPER2_STRET_FIXUP_DECL]
#define objc2_begin_catch_decl objc_v2_global_trees[OCTI_V2_BEGIN_CATCH_DECL]
#define objc2_end_catch_decl objc_v2_global_trees[OCTI_V2_END_CATCH_DECL]
#define objc_rethrow_exception_decl \
objc_v2_global_trees[OCTI_V2_RETHROW_DECL]
/* rt_trees identifiers - shared between NeXT implementations. These allow
the FE to tag meta-data in a manner that survives LTO and can be used when
the runtime requires that certain meta-data items appear in particular
named sections. */
#include "objc-next-metadata-tags.h"
extern GTY(()) tree objc_rt_trees[OCTI_RT_META_MAX];
/* The OCTI_V2_... enumeration itself is in above. */
static GTY(()) tree objc_v2_global_trees[OCTI_V2_MAX];
static void next_runtime_02_initialize (void);
static void build_v2_message_ref_templates (void);
static void build_v2_class_templates (void);
static void build_v2_super_template (void);
static void build_v2_category_template (void);
static void build_v2_protocol_template (void);
static tree next_runtime_abi_02_super_superclassfield_id (void);
static tree next_runtime_abi_02_class_decl (tree);
static tree next_runtime_abi_02_metaclass_decl (tree);
static tree next_runtime_abi_02_category_decl (tree);
static tree next_runtime_abi_02_protocol_decl (tree);
static tree next_runtime_abi_02_string_decl (tree, const char *, string_section);
static tree next_runtime_abi_02_get_class_reference (tree);
static tree next_runtime_abi_02_build_selector_reference (location_t, tree, tree);
static tree next_runtime_abi_02_get_protocol_reference (location_t, tree);
static tree next_runtime_abi_02_build_ivar_ref (location_t, tree, tree);
static tree next_runtime_abi_02_get_class_super_ref (location_t, struct imp_entry *, bool);
static tree next_runtime_abi_02_get_category_super_ref (location_t, struct imp_entry *, bool);
static tree next_runtime_abi_02_receiver_is_class_object (tree);
static void next_runtime_abi_02_get_arg_type_list_base (vec<tree, va_gc> **,
tree, int, int);
static tree next_runtime_abi_02_build_objc_method_call (location_t, tree, tree,
tree, tree, tree, int);
static bool next_runtime_abi_02_setup_const_string_class_decl (void);
static tree next_runtime_abi_02_build_const_string_constructor (location_t, tree, int);
static tree create_extern_decl (tree, const char *);
static void objc_generate_v2_next_metadata (void);
static bool objc2_objc_exception_attr (tree);
/* void build_v2_protocol_reference (tree);*/
static void build_v2_ehtype_template (void);
static void build_v2_eh_catch_objects (void);
static tree next_runtime_02_eh_type (tree);
static tree objc_eh_personality (void);
static tree build_throw_stmt (location_t, tree, bool);
static tree objc_build_exc_ptr (struct objc_try_context **);
static tree begin_catch (struct objc_try_context **, tree, tree, tree, bool);
static void finish_catch (struct objc_try_context **, tree);
static tree finish_try_stmt (struct objc_try_context **);
/* TODO: Use an objc-map. */
static GTY ((length ("SIZEHASHTABLE"))) hash *extern_names;
bool
objc_next_runtime_abi_02_init (objc_runtime_hooks *rthooks)
{
extern_names = ggc_cleared_vec_alloc<hash> (SIZEHASHTABLE);
if (flag_objc_sjlj_exceptions)
{
inform (UNKNOWN_LOCATION,
"%<-fobjc-sjlj-exceptions%> is ignored for "
"%<-fnext-runtime%> when %<-fobjc-abi-version%> "
"greater than 1");
flag_objc_sjlj_exceptions = 0;
}
/* NeXT ABI 2 is intended to default to checking for nil receivers. */
if (! global_options_set.x_flag_objc_nilcheck)
flag_objc_nilcheck = 1;
rthooks->initialize = next_runtime_02_initialize;
rthooks->default_constant_string_class_name = DEF_CONSTANT_STRING_CLASS_NAME;
rthooks->tag_getclass = TAG_GETCLASS;
rthooks->super_superclassfield_ident = next_runtime_abi_02_super_superclassfield_id;
rthooks->class_decl = next_runtime_abi_02_class_decl;
rthooks->metaclass_decl = next_runtime_abi_02_metaclass_decl;
rthooks->category_decl = next_runtime_abi_02_category_decl;
rthooks->protocol_decl = next_runtime_abi_02_protocol_decl;
rthooks->string_decl = next_runtime_abi_02_string_decl;
rthooks->get_class_reference = next_runtime_abi_02_get_class_reference;
rthooks->build_selector_reference = next_runtime_abi_02_build_selector_reference;
rthooks->get_protocol_reference = next_runtime_abi_02_get_protocol_reference;
rthooks->build_ivar_reference = next_runtime_abi_02_build_ivar_ref;
rthooks->get_class_super_ref = next_runtime_abi_02_get_class_super_ref;
rthooks->get_category_super_ref = next_runtime_abi_02_get_category_super_ref;
rthooks->receiver_is_class_object = next_runtime_abi_02_receiver_is_class_object;
rthooks->get_arg_type_list_base = next_runtime_abi_02_get_arg_type_list_base;
rthooks->build_objc_method_call = next_runtime_abi_02_build_objc_method_call;
rthooks->setup_const_string_class_decl =
next_runtime_abi_02_setup_const_string_class_decl;
rthooks->build_const_string_constructor =
next_runtime_abi_02_build_const_string_constructor;
rthooks->build_throw_stmt = build_throw_stmt;
rthooks->build_exc_ptr = objc_build_exc_ptr;
rthooks->begin_catch = begin_catch;
rthooks->finish_catch = finish_catch;
rthooks->finish_try_stmt = finish_try_stmt;
rthooks->generate_metadata = objc_generate_v2_next_metadata;
return true;
}
/* We need a way to convey what kind of meta-data are represented by a given
variable, since each type is expected (by the runtime) to be found in a
specific named section. The solution must be usable with LTO.
The scheme used for NeXT ABI 0/1 (partial matching of variable names) is not
satisfactory when LTO is used with ABI-2. We now tag ObjC meta-data with
identification attributes in the front end. The back-end may choose to act
on these as it requires. */
static void
next_runtime_abi_02_init_metadata_attributes (void)
{
if (!objc_meta)
objc_meta = get_identifier ("OBJC2META");
if (!meta_base)
meta_base = get_identifier ("V2_BASE");
meta_class = get_identifier ("G2_CLAS");
meta_metaclass = get_identifier ("G2_META");
meta_category = meta_base;
meta_protocol = get_identifier ("V2_PCOL");
meta_clac_vars =
meta_clai_vars = meta_base;
meta_clac_meth =
meta_clai_meth =
meta_catc_meth =
meta_cati_meth =
meta_proto_cls_meth =
meta_proto_nst_meth = meta_base;
meta_clas_prot =
meta_catg_prot = meta_base;
meta_sel_refs = get_identifier ("V2_SRFS");
meta_class_name = get_identifier ("V2_CNAM");
meta_meth_name = get_identifier ("V2_MNAM");
meta_meth_type = get_identifier ("V2_MTYP");
meta_prop_name_attr = get_identifier ("V2_STRG");
meta_mref = get_identifier ("V2_MREF");
meta_class_ref = get_identifier ("V2_CLRF");
meta_superclass_ref = get_identifier ("V2_SURF");
meta_label_classlist = get_identifier ("V2_CLAB");
meta_label_nonlazy_classlist = get_identifier ("V2_NLCL");
meta_label_categorylist = get_identifier ("V2_CALA");
meta_label_nonlazy_categorylist = get_identifier ("V2_NLCA");
meta_label_protocollist = get_identifier ("V2_PLST");
meta_proto_ref = get_identifier ("V2_PRFS");
meta_info = get_identifier ("V2_INFO");
meta_ehtype = get_identifier ("V2_EHTY");
meta_const_str = get_identifier ("V2_CSTR");
meta_ivar_ref = get_identifier ("V2_IVRF");
}
static void next_runtime_02_initialize (void)
{
tree type;
#ifdef OBJCPLUS
/* For all NeXT objc ABIs -fobjc-call-cxx-cdtors is on by
default. */
if (!global_options_set.x_flag_objc_call_cxx_cdtors)
global_options.x_flag_objc_call_cxx_cdtors = 1;
#endif
/* Set up attributes to be attached to the meta-data so that they
will be placed in the correct sections. */
next_runtime_abi_02_init_metadata_attributes ();
/* `struct objc_selector *' */
objc_selector_type = build_pointer_type (xref_tag (RECORD_TYPE,
get_identifier (TAG_SELECTOR)));
/* SEL typedef. */
type = lang_hooks.decls.pushdecl (build_decl (input_location,
TYPE_DECL,
objc_selector_name,
objc_selector_type));
suppress_warning (type);
/* IMP : id (*) (id, _message_ref_t*, ...)
SUPER_IMP : id (*) ( super_t*, _super_message_ref_t*, ...)
objc_v2_selector_type. */
build_v2_message_ref_templates ();
objc_v2_ivar_list_ptr =
build_pointer_type (xref_tag (RECORD_TYPE,
get_identifier ("_ivar_list_t")));
objc_prop_list_ptr =
build_pointer_type (xref_tag (RECORD_TYPE,
get_identifier ("_prop_list_t")));
build_v2_class_templates ();
build_v2_super_template ();
build_v2_protocol_template ();
build_v2_category_template ();
bool fixup_p = flag_next_runtime < USE_FIXUP_BEFORE;
if (fixup_p)
{
/* id objc_msgSend_fixup_rtp (id, struct message_ref_t*, ...); */
type = build_varargs_function_type_list (objc_object_type,
objc_object_type,
objc_v2_selector_type,
NULL_TREE);
}
else
{
/* id objc_msgSendXXXX (id, SEL, ...); */
type = build_varargs_function_type_list (objc_object_type,
objc_object_type,
objc_selector_type,
NULL_TREE);
}
const char *fnam = fixup_p ? TAG_MSGSEND TAG_FIXUP : TAG_MSGSEND;
umsg_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (umsg_fixup_decl) = 0;
/* id objc_msgSend_stret_fixup_rtp (id, struct message_ref_t*, ...); */
fnam = fixup_p ? TAG_MSGSEND_STRET TAG_FIXUP : TAG_MSGSEND_STRET;
umsg_stret_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (umsg_stret_fixup_decl) = 0;
/* id objc_msgSendId_fixup_rtp (id, struct message_ref_t*, ...); */
fnam = fixup_p ? TAG_MSGSENDID TAG_FIXUP : TAG_MSGSENDID;
umsg_id_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (umsg_id_fixup_decl) = 0;
/* id objc_msgSendId_stret_fixup_rtp (id, struct message_ref_t*, ...); */
fnam = fixup_p ? TAG_MSGSENDID_STRET TAG_FIXUP : TAG_MSGSENDID_STRET;
umsg_id_stret_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (umsg_id_stret_fixup_decl) = 0;
/* id objc_msgSendSuper2_fixup_rtp
(struct objc_super *, struct message_ref_t*, ...); */
type = build_varargs_function_type_list (objc_object_type,
objc_super_type,
objc_v2_super_selector_type,
NULL_TREE);
fnam = fixup_p ? TAG_MSGSENDSUPER TAG_FIXUP : TAG_MSGSENDSUPER;
umsg_id_super2_fixup_decl = add_builtin_function (fnam, type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (umsg_id_super2_fixup_decl) = 0;
/* id objc_msgSendSuper2_stret_fixup_rtp
(struct objc_super *, struct message_ref_t*, ...); */
fnam = fixup_p ? TAG_MSGSENDSUPER_STRET TAG_FIXUP : TAG_MSGSENDSUPER_STRET;
umsg_id_super2_stret_fixup_decl = add_builtin_function (fnam, type, 0,
NOT_BUILT_IN, NULL,
NULL_TREE);
TREE_NOTHROW (umsg_id_super2_stret_fixup_decl) = 0;
/* Present in the library, but unused by the FE. */
/* Protocol *objc_getProtocol (const char *)
type = build_function_type_list (objc_protocol_type,
const_string_type_node,
NULL_TREE);
objc_v2_getprotocol_decl = add_builtin_function ("objc_getProtocol",
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (objc_v2_getprotocol_decl) = 0;*/
UOBJC_V2_CACHE_decl = create_extern_decl (ptr_type_node,
"_objc_empty_cache");
UOBJC_V2_VTABLE_decl = create_extern_decl (objc_v2_imp_type,
"_objc_empty_vtable");
/* id objc_getClass (const char *); */
type = build_function_type_list (objc_object_type,
const_string_type_node,
NULL_TREE);
objc_get_class_decl = add_builtin_function (TAG_GETCLASS,
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
/* id objc_getMetaClass (const char *); */
objc_get_meta_class_decl = add_builtin_function (TAG_GETMETACLASS,
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
/* This is the type of all of the following functions
objc_copyStruct(). */
type = build_function_type_list (void_type_node,
ptr_type_node,
const_ptr_type_node,
ptrdiff_type_node,
boolean_type_node,
boolean_type_node,
NULL_TREE);
/* Declare the following function:
void
objc_copyStruct (void *destination, const void *source,
ptrdiff_t size, BOOL is_atomic, BOOL has_strong); */
objc_copyStruct_decl = add_builtin_function ("objc_copyStruct",
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (objc_copyStruct_decl) = 0;
objc_getPropertyStruct_decl = NULL_TREE;
objc_setPropertyStruct_decl = NULL_TREE;
gcc_checking_assert (!flag_objc_sjlj_exceptions);
/* Although we warn that fobjc-exceptions is required for exceptions
code, we carry on and create it anyway. */
/* This can be required, even when exceptions code is not present,
when an __attribute__((objc_exception)) is applied to a
class. */
build_v2_ehtype_template ();
/* void * objc_begin_catch (void *) */
type = build_function_type_list (ptr_type_node,
ptr_type_node, NULL_TREE);
objc2_begin_catch_decl = add_builtin_function ("objc_begin_catch",
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (objc2_begin_catch_decl) = 0;
/* void objc_end_catch () */
type = build_function_type_list (void_type_node, NULL_TREE);
objc2_end_catch_decl = add_builtin_function ("objc_end_catch",
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (objc2_end_catch_decl) = 0;
/* void objc_exception_rethrow (void) */
objc_rethrow_exception_decl =
add_builtin_function ("objc_exception_rethrow",
type, 0, NOT_BUILT_IN,
NULL, NULL_TREE);
TREE_NOTHROW (objc_rethrow_exception_decl) = 0;
using_eh_for_cleanups ();
lang_hooks.eh_runtime_type = next_runtime_02_eh_type;
lang_hooks.eh_personality = objc_eh_personality;
}
/* NOTE --- templates --- */
/* Set 'objc_v2_message_ref_template' to the data type node for
'struct _message_ref_t'. This needs to be done just once per
compilation. Also Set 'objc_v2_super_message_ref_template' to data
type node for 'struct _super_message_ref_t'. */
/* struct _message_ref_t
{
IMP messenger;
SEL name;
};
where IMP is: id (*) (id, _message_ref_t*, ...)
*/
/* struct _super_message_ref_t
{
SUPER_IMP messenger;
SEL name;
};
where SUPER_IMP is: id (*) ( super_t*, _super_message_ref_t*, ...)
*/
static void
build_v2_message_ref_templates (void)
{
tree ptr_message_ref_t;
tree decls, *chain = NULL;
/* struct _message_ref_t {...} */
objc_v2_message_ref_template =
objc_start_struct (get_identifier ("_message_ref_t"));
/* IMP messenger; */
ptr_message_ref_t =
build_pointer_type (xref_tag (RECORD_TYPE,
get_identifier ("_message_ref_t")));
objc_v2_imp_type =
build_pointer_type (build_function_type_list
(objc_object_type,
objc_object_type,
ptr_message_ref_t,
NULL_TREE));
decls = add_field_decl (objc_v2_imp_type, "messenger", &chain);
/* SEL name; */
add_field_decl (objc_selector_type, "name", &chain);
objc_finish_struct (objc_v2_message_ref_template, decls);
objc_v2_selector_type = build_pointer_type (objc_v2_message_ref_template);
chain = NULL;
/* struct _super_message_ref_t {...} */
objc_v2_super_message_ref_template =
objc_start_struct (get_identifier ("_super_message_ref_t"));
/* SUPER_IMP messenger; */
ptr_message_ref_t = build_pointer_type
(xref_tag (RECORD_TYPE,
get_identifier ("_super_message_ref_t")));
objc_v2_super_imp_type =
build_pointer_type (build_function_type_list
(objc_object_type,
objc_super_type,
ptr_message_ref_t,
NULL_TREE));
add_field_decl (objc_v2_super_imp_type, "messenger", &chain);
/* SEL name; */
add_field_decl (objc_selector_type, "name", &chain);
objc_finish_struct (objc_v2_super_message_ref_template, decls);
objc_v2_super_selector_type =
build_pointer_type (objc_v2_super_message_ref_template);
}
/* Build following types which represent each class implementation.
struct class_ro_t
{
uint32_t const flags;
uint32_t const instanceStart;
uint32_t const instanceSize;
#ifdef __LP64__
uint32_t const reserved;
#endif
const uint8_t * const ivarLayout;
const char *const name;
const struct method_list_t * const baseMethods;
const struct objc_protocol_list *const baseProtocols;
const struct ivar_list_t *const ivars;
const uint8_t * const weakIvarLayout;
const struct _prop_list_t * const properties;
};
struct class_t
{
struct class_t *isa;
struct class_t *superclass;
void *cache;
IMP *vtable;
...When this is active - it will point to a rw version, but
when we build the meta-data we point it to the ro...
struct class_ro_t *data;
};
*/
static void
build_v2_class_templates (void)
{
tree cnst_strg_type;
tree decls, *chain = NULL;
/* struct class_ro_t {...} */
objc_v2_class_ro_template =
objc_start_struct (get_identifier (UTAG_V2_CLASS_RO));
/* uint32_t const flags; */
decls = add_field_decl (integer_type_node, "flags", &chain);
/* uint32_t const instanceStart; */
add_field_decl (integer_type_node, "instanceStart", &chain);
/* uint32_t const instanceSize; */
add_field_decl (integer_type_node, "instanceSize", &chain);
/* This ABI is currently only used on m64 NeXT. We always
explicitly declare the alignment padding. */
/* uint32_t const reserved; */
add_field_decl (integer_type_node, "reserved", &chain);
/* const uint8_t * const ivarLayout; */
cnst_strg_type = build_pointer_type (unsigned_char_type_node);
add_field_decl (cnst_strg_type, "ivarLayout", &chain);
/* const char *const name; */
add_field_decl (string_type_node, "name", &chain);
/* const struct method_list_t * const baseMethods; */
add_field_decl (objc_method_list_ptr, "baseMethods", &chain);
/* const struct objc_protocol_list *const baseProtocols; */
add_field_decl (build_pointer_type
(xref_tag (RECORD_TYPE,
get_identifier (UTAG_V2_PROTOCOL_LIST))),
"baseProtocols", &chain);
/* const struct ivar_list_t *const ivars; */
add_field_decl (objc_v2_ivar_list_ptr, "ivars", &chain);
/* const uint8_t * const weakIvarLayout; */
add_field_decl (cnst_strg_type, "weakIvarLayout", &chain);
/* struct _prop_list_t * baseProperties; */
add_field_decl (objc_prop_list_ptr, "baseProperties", &chain);
objc_finish_struct (objc_v2_class_ro_template, decls);
chain = NULL;
/* struct class_t {...} */
objc_v2_class_template =
objc_start_struct (get_identifier (UTAG_V2_CLASS));
/* struct class_t *isa; */
decls = add_field_decl (build_pointer_type (objc_v2_class_template),
"isa", &chain);
/* struct class_t * const superclass; */
add_field_decl (build_pointer_type (objc_v2_class_template),
"superclass", &chain);
/* void *cache; */
add_field_decl (build_pointer_type (void_type_node), "cache", &chain);
/* IMP *vtable; */
add_field_decl (build_pointer_type (objc_v2_imp_type), "vtable", &chain);
/* struct class_ro_t *ro; */
add_field_decl (build_pointer_type (objc_v2_class_ro_template), "ro", &chain);
objc_finish_struct (objc_v2_class_template, decls);
}
/* struct _objc_super
{
struct _objc_object *self;
Class cls;
}; */
void
build_v2_super_template (void)
{
tree decls, *chain = NULL;
objc_super_template = objc_start_struct (get_identifier (UTAG_SUPER));
/* struct _objc_object *self; */
decls = add_field_decl (objc_object_type, "self", &chain);
/* Class cls; */
add_field_decl (objc_class_type, "cls", &chain);
objc_finish_struct (objc_super_template, decls);
}
/* struct protocol_t
{
Class isa;
const char * const protocol_name;
const struct protocol_list_t * const protocol_list;
const struct method_list_t * const instance_methods;
const struct method_list_t * const class_methods;
const struct method_list_t * optionalInstanceMethods;
const struct method_list_t * optionalClassMethod
const struct _prop_list_t * const properties;
const uint32_t size;
const uint32_t flags;
const char ** extended_method_types;
const char * demangled_name;
const struct _prop_list_t * class_properties;
}
*/
static void
build_v2_protocol_template (void)
{
tree decls, *chain = NULL;
objc_v2_protocol_template =
objc_start_struct (get_identifier (UTAG_V2_PROTOCOL));
/* Class isa; */
decls = add_field_decl (objc_object_type, "isa", &chain);
/* char *protocol_name; */
add_field_decl (string_type_node, "protocol_name", &chain);
/* const struct protocol_list_t * const protocol_list; */
add_field_decl (build_pointer_type (objc_v2_protocol_template),
"protocol_list", &chain);
/* const struct method_list_t * const instance_methods; */
add_field_decl (objc_method_proto_list_ptr, "instance_methods", &chain);
/* const struct method_list_t * const class_methods; */
add_field_decl (objc_method_proto_list_ptr, "class_methods", &chain);
/* const struct method_list_t * optionalInstanceMethods; */
add_field_decl (objc_method_proto_list_ptr, "optionalInstanceMethods", &chain);
/* const struct method_list_t * optionalClassMethods; */
add_field_decl (objc_method_proto_list_ptr, "optionalClassMethods", &chain);
/* struct _prop_list_t * properties; */
add_field_decl (objc_prop_list_ptr, "properties", &chain);
/* const uint32_t size; */
add_field_decl (integer_type_node, "size", &chain);
/* const uint32_t flags; */
add_field_decl (integer_type_node, "flags", &chain);
/* const char **extendedMethodTypes; */
tree ptr_to_ptr_to_char = build_pointer_type (string_type_node);
add_field_decl (ptr_to_ptr_to_char, "extended_method_types", &chain);
/* const char *demangledName; */
add_field_decl (string_type_node, "demangled_name", &chain);
/* const struct _prop_list_t *class_properties; */
add_field_decl (objc_prop_list_ptr, "class_properties", &chain);
objc_finish_struct (objc_v2_protocol_template, decls);
}
/* Build type for a category:
struct category_t
{
const char * const name;
struct class_t *const cls;
const struct method_list_t * const instance_methods;
const struct method_list_t * const class_methods;
const struct protocol_list_t * const protocols;
const struct _prop_list_t * const properties;
}
*/
static void
build_v2_category_template (void)
{
tree decls, *chain = NULL;
objc_v2_category_template =
objc_start_struct (get_identifier ("_category_t"));
/* char *name; */
decls = add_field_decl (string_type_node, "name", &chain);
/* struct class_t *const cls; */
add_field_decl (build_pointer_type (objc_v2_class_template), "cls", &chain);
/* struct method_list_t *instance_methods; */
add_field_decl (objc_method_list_ptr, "instance_methods", &chain);
/* struct method_list_t *class_methods; */
add_field_decl (objc_method_list_ptr, "class_methods", &chain);
/* struct protocol_list_t *protocol_list; */
add_field_decl (build_pointer_type (objc_v2_protocol_template),
"protocol_list", &chain );
/* struct _prop_list_t * properties; */
add_field_decl (objc_prop_list_ptr, "properties", &chain);
objc_finish_struct (objc_v2_category_template, decls);
}
/* NOTE --- Decls, Identifiers, Names etc. --- */
/* This routine is given a name and returns a matching extern variable
if one is found. */
static tree
hash_name_lookup (hash *hashlist, tree name)
{
hash target;
target = hashlist[IDENTIFIER_HASH_VALUE (name) % SIZEHASHTABLE];
while (target)
{
if (name == DECL_NAME (target->key))
return target->key;
target = target->next;
}
return 0;
}
/* This routine is given an extern variable and enters it in its hash
table. Note that hashing is done on its inner IDENTIFIER_NODE
node. */
static void
hash_name_enter (hash *hashlist, tree id)
{
hash obj;
int slot = IDENTIFIER_HASH_VALUE (DECL_NAME (id)) % SIZEHASHTABLE;
obj = ggc_alloc<hashed_entry> ();
obj->list = 0;
obj->next = hashlist[slot];
obj->key = id;
hashlist[slot] = obj; /* append to front */
}
/* Create a declaration "extern <type> <name>;"
The var will need to be finalized (e.g. by calling finish_var_decl()). */
static tree
create_extern_decl (tree type, const char *name)
{
tree id = get_identifier (name);
tree var = hash_name_lookup (extern_names, id);
if (var)
return var;
/* New name. */
var = start_var_decl (type, name);
TREE_STATIC (var) = 0;
DECL_EXTERNAL (var) = 1;
TREE_PUBLIC (var) = 1;
hash_name_enter (extern_names, var);
return var;
}
/* Create a globally visible definition for variable NAME of a given TYPE. The
finish_var_decl() routine will need to be called on it afterwards. */
static tree
create_global_decl (tree type, const char *name, bool is_def = false);
static tree
create_global_decl (tree type, const char *name, bool is_def)
{
tree id = get_identifier (name);
tree var = hash_name_lookup (extern_names, id);
if (var)
is_def = true;
else
{
var = start_var_decl (type, name);
hash_name_enter (extern_names, var);
}
if (is_def)
{
DECL_EXTERNAL (var) = 0;
TREE_STATIC (var) = 1;
}
TREE_PUBLIC (var) = 1;
return var;
}
/* Create a symbol with __attribute__ ((visibility ("hidden")))
attribute (private extern). */
static tree
create_hidden_decl (tree type, const char *name, bool is_def = false);
static tree
create_hidden_decl (tree type, const char *name, bool is_def)
{
tree decl = create_global_decl (type, name, is_def);
DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN;
DECL_VISIBILITY_SPECIFIED (decl) = 1;
return decl;
}
/* Irritatingly, we have a different superclass field name for ABI=2. */
/* PS/TODO: The field name does not matter, it is only used internally
by the compiler. We can rename it to whatever we want. ;-) */
static tree
next_runtime_abi_02_super_superclassfield_id (void)
{
/* TODO: Simplify. Just always return get_identifier ("cls"), or at
most look it once at startup then always return it. */
if (!super_superclassfield_id)
super_superclassfield_id = get_identifier ("cls");
return super_superclassfield_id;
}
static tree
next_runtime_abi_02_class_decl (tree klass)
{
tree decl;
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "OBJC_CLASS_$_%s",
IDENTIFIER_POINTER (CLASS_NAME (klass)));
/* ObjC2 classes are extern visible. */
decl = create_global_decl (objc_v2_class_template, buf);
OBJCMETA (decl, objc_meta, meta_class);
return decl;
}
static tree
next_runtime_abi_02_metaclass_decl (tree klass)
{
tree decl;
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "OBJC_METACLASS_$_%s",
IDENTIFIER_POINTER (CLASS_NAME (klass)));
/* ObjC2 classes are extern visible. */
decl = create_global_decl (objc_v2_class_template, buf);
OBJCMETA (decl, objc_meta, meta_metaclass);
return decl;
}
static tree
next_runtime_abi_02_category_decl (tree klass)
{
tree decl;
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "_OBJC_Category_%s_%s",
IDENTIFIER_POINTER (CLASS_NAME (klass)),
IDENTIFIER_POINTER (CLASS_SUPER_NAME (klass)));
decl = start_var_decl (objc_v2_category_template, buf);
OBJCMETA (decl, objc_meta, meta_category);
return decl;
}
static tree
next_runtime_abi_02_protocol_decl (tree p)
{
tree decl;
char buf[BUFSIZE];
/* static struct _objc_protocol _OBJC_Protocol_<mumble>; */
snprintf (buf, BUFSIZE, "_OBJC_Protocol_%s",
IDENTIFIER_POINTER (PROTOCOL_NAME (p)));
if (flag_next_runtime >= USE_FIXUP_BEFORE)
{
decl = create_hidden_decl (objc_v2_protocol_template, buf);
DECL_WEAK (decl) = true;
}
else
decl = start_var_decl (objc_v2_protocol_template, buf);
OBJCMETA (decl, objc_meta, meta_protocol);
return decl;
}
static tree
next_runtime_abi_02_string_decl (tree type, const char *name, string_section where)
{
tree var = start_var_decl (type, name);
switch (where)
{
case class_names:
OBJCMETA (var, objc_meta, meta_class_name);
break;
case meth_var_names:
OBJCMETA (var, objc_meta, meta_meth_name);
break;
case meth_var_types:
OBJCMETA (var, objc_meta, meta_meth_type);
break;
case prop_names_attr:
OBJCMETA (var, objc_meta, meta_prop_name_attr);
break;
default:
OBJCMETA (var, objc_meta, meta_base);
break;
}
return var;
}
/* NOTE --- entry --- */
struct GTY(()) ident_data_tuple {
tree ident;
tree data;
};
/* This routine creates a file scope static variable of type 'Class'
to hold the address of a class. */
static tree
build_v2_class_reference_decl (tree ident)
{
tree decl;
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "_OBJC_ClassRef_%s", IDENTIFIER_POINTER (ident));
decl = start_var_decl (objc_class_type, buf);
OBJCMETA (decl, objc_meta, meta_class_ref);
return decl;
}
/* This routine builds a class refs entry for each class name used.
Initially, a (static-ref, IDENT) tuple is added to the list. The
ident is replaced with address of the class metadata (of type
'Class') in the output routine. */
static GTY (()) vec<ident_data_tuple, va_gc> *classrefs;
static tree
objc_v2_get_class_reference (tree ident)
{
tree decl;
ident_data_tuple e;
if (classrefs)
{
int count;
ident_data_tuple *ref;
FOR_EACH_VEC_ELT (*classrefs, count, ref)
{
if (ref->ident == ident)
{
if (!ref->data)
ref->data = build_v2_class_reference_decl (ident);
return ref->data;
}
}
}
else
/* Somewhat arbitrary initial provision. */
vec_alloc (classrefs, 16);
/* We come here if we don't find the entry - or if the table was yet
to be created. */
decl = build_v2_class_reference_decl (ident);
e.ident = ident;
e.data = decl;
vec_safe_push (classrefs, e);
return decl;
}
static tree
next_runtime_abi_02_get_class_reference (tree ident)
{
if (!flag_zero_link)
return objc_v2_get_class_reference (ident);
else
{
/* We fall back to using objc_getClass (). */
vec<tree, va_gc> *v;
vec_alloc (v, 1);
tree t;
/* ??? add_class_reference (ident); - is pointless, since the
system lib does not export the equivalent symbols. Maybe we
need to build a class ref anyway. */
t = my_build_string_pointer (IDENTIFIER_LENGTH (ident) + 1,
IDENTIFIER_POINTER (ident));
v->quick_push (t);
t = build_function_call_vec (input_location, vNULL, objc_get_class_decl,
v, 0);
vec_free (v);
return t;
}
}
/* Used by build_function_type_for_method. Append the types for
receiver & _cmd at the start of a method argument list to ARGTYPES.
CONTEXT is either METHOD_DEF or METHOD_REF, saying whether we are
trying to define a method or call one. SUPERFLAG says this is for a
send to super. METH may be NULL, in the case that there is no
prototype. */
static void
next_runtime_abi_02_get_arg_type_list_base (vec<tree, va_gc> **argtypes,
tree meth, int context,
int superflag)
{
tree receiver_type;
if (superflag)
receiver_type = objc_super_type;
else if (context == METHOD_DEF && TREE_CODE (meth) == INSTANCE_METHOD_DECL)
receiver_type = objc_instance_type;
else
receiver_type = objc_object_type;
vec_safe_push (*argtypes, receiver_type);
if (flag_next_runtime < USE_FIXUP_BEFORE)
/* Selector type - will eventually change to `int'. */
vec_safe_push (*argtypes, superflag ? objc_v2_super_selector_type
: objc_v2_selector_type);
else
vec_safe_push (*argtypes, objc_selector_type);
}
/* TODO: Merge this with the message refs. */
static tree
build_selector_reference_decl (tree ident)
{
tree decl;
char *t, buf[BUFSIZE];
snprintf (buf, BUFSIZE, "_OBJC_SelRef_%s", IDENTIFIER_POINTER (ident));
t = buf;
while (*t)
{
if (*t==':')
*t = '$'; /* Underscore would clash between foo:bar and foo_bar. */
t++;
}
decl = start_var_decl (objc_selector_type, buf);
OBJCMETA (decl, objc_meta, meta_sel_refs);
return decl;
}
static tree
next_runtime_abi_02_build_selector_reference (location_t loc ATTRIBUTE_UNUSED,
tree ident,
tree proto ATTRIBUTE_UNUSED)
{
tree *chain = &sel_ref_chain;
tree expr;
while (*chain)
{
if (TREE_VALUE (*chain) == ident)
return TREE_PURPOSE (*chain);
chain = &TREE_CHAIN (*chain);
}
expr = build_selector_reference_decl (ident);
*chain = tree_cons (expr, ident, NULL_TREE);
return expr;
}
/* Declare a variable of type 'struct message_ref_t'. */
/* This will be finished in build_v2_message_ref_translation_table ().
We take an idea from LLVM in making the names a bit more connected
and thus the asm more readable. */
static tree
build_v2_message_reference_decl (tree sel_name, tree message_func_ident)
{
tree decl;
char buf[BUFSIZE], *t;
int offset = 12;
/* Skip past the objc_msgSend it's the same for all... */
if (IDENTIFIER_POINTER (message_func_ident)[offset] == '_')
offset++;
snprintf (buf, BUFSIZE, "_OBJC_MsgRef_%s_%s",
&(IDENTIFIER_POINTER (message_func_ident)[offset]),
IDENTIFIER_POINTER (sel_name));
t = buf;
while (*t)
{
if (*t==':')
*t = '$'; /* Underscore would clash between foo:bar and foo_bar. */
t++;
}
decl = start_var_decl (objc_v2_message_ref_template, buf);
OBJCMETA (decl, objc_meta, meta_mref);
return decl;
}
struct GTY(()) msgref_entry {
tree func;
tree selname;
tree refdecl;
};
static GTY (()) vec<msgref_entry, va_gc> *msgrefs;
/* Build the list of (objc_msgSend_fixup_xxx, selector name), used
later on to initialize the table of 'struct message_ref_t'
elements. */
static tree
build_v2_selector_messenger_reference (tree sel_name, tree message_func_decl)
{
tree decl;
msgref_entry e;
if (msgrefs)
{
int count;
msgref_entry *ref;
FOR_EACH_VEC_ELT (*msgrefs, count, ref)
if (ref->func == message_func_decl && ref->selname == sel_name)
return ref->refdecl;
}
else
/* Somewhat arbitrary initial provision. */
vec_alloc (msgrefs, 32);
/* We come here if we don't find a match or at the start. */
decl = build_v2_message_reference_decl (sel_name,
DECL_NAME (message_func_decl));
e.func = message_func_decl;
e.selname = sel_name;
e.refdecl = decl;
vec_safe_push (msgrefs, e);
return decl;
}
static tree
build_v2_protocollist_ref_decl (tree protocol)
{
tree decl;
tree protocol_ident = PROTOCOL_NAME (protocol);
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "_OBJC_ProtocolRef_%s",
IDENTIFIER_POINTER (protocol_ident));
/* TODO: other compiler versions make these hidden & weak. */
decl = create_global_decl (objc_protocol_type, buf);
/* Let optimizer know that this decl is not removable. */
DECL_PRESERVE_P (decl) = 1;
OBJCMETA (decl, objc_meta, meta_proto_ref);
return decl;
}
struct GTY(()) prot_list_entry {
tree id;
tree refdecl;
};
static GTY (()) vec<prot_list_entry, va_gc> *protrefs;
static tree
objc_v2_get_protocol_reference (tree ident)
{
tree decl;
prot_list_entry e;
if (protrefs)
{
int count;
prot_list_entry *ref;
FOR_EACH_VEC_ELT (*protrefs, count, ref)
{
if (ref->id == ident)
{
if (!ref->refdecl)
ref->refdecl = build_v2_protocollist_ref_decl (ident);
return ref->refdecl;
}
}
}
else
/* Somewhat arbitrary initial provision. */
vec_alloc (protrefs, 32);
/* We come here if we don't find the entry - or if the table was yet
to be created. */
decl = build_v2_protocollist_ref_decl (ident);
e.id = ident;
e.refdecl = decl;
vec_safe_push (protrefs, e);
return decl;
}
static tree
next_runtime_abi_02_get_protocol_reference (location_t loc ATTRIBUTE_UNUSED,
tree p)
{
if (!PROTOCOL_FORWARD_DECL (p))
PROTOCOL_FORWARD_DECL (p) = next_runtime_abi_02_protocol_decl (p);
return objc_v2_get_protocol_reference (p);
}
/* This routine returns the ivar declaration, if component is a valid
ivar field; NULL_TREE otherwise. On finding an ivar, it also
returns the class name in CLASS. */
static tree
objc_is_ivar (tree expr, tree component, tree *klass)
{
tree field = NULL_TREE;
tree basetype = TYPE_MAIN_VARIANT (TREE_TYPE (expr));
if (TREE_CODE (basetype) == RECORD_TYPE
&& TYPE_HAS_OBJC_INFO (basetype) && TYPE_OBJC_INTERFACE (basetype))
{
*klass = lookup_interface (OBJC_TYPE_NAME (basetype));
if (*klass)
{
do
{
tree ivar_chain = CLASS_RAW_IVARS (*klass);
if (ivar_chain)
{
field = is_ivar (ivar_chain, component);
if (field != NULL_TREE)
break;
}
*klass = lookup_interface (CLASS_SUPER_NAME (*klass));
}
while (*klass);
}
}
return field;
}
static void
create_ivar_offset_name (char *buf, tree class_name, tree field_decl)
{
tree fname = DECL_NAME (field_decl);
sprintf (buf, "OBJC_IVAR_$_%s.%s", IDENTIFIER_POINTER (class_name),
IDENTIFIER_POINTER (fname));
return;
}
/* This routine generates new abi's ivar reference tree. It amounts
to generating *(TYPE*)((char*)pObj + OFFSET_IVAR) when we normally
generate pObj->IVAR. OFFSET_IVAR is an 'extern' variable holding
the offset for 'IVAR' field. TYPE is type of IVAR field. */
static tree
objc_v2_build_ivar_ref (tree datum, tree component)
{
tree field, ref, class_name, offset, ftype, expr;
char var_offset_name[512];
field = objc_is_ivar (datum, component, &class_name);
if (!field)
return NULL_TREE;
/* This routine only handles non-bitfield fields */
if (DECL_C_BIT_FIELD (field))
return NULL_TREE;
create_ivar_offset_name (var_offset_name, CLASS_NAME (class_name), field);
offset = create_extern_decl (TREE_TYPE (size_zero_node), var_offset_name);
ftype = TREE_TYPE (field);
/* (char*)datum */
expr = build_c_cast (input_location,
string_type_node, build_fold_addr_expr (datum));
/* (char*)datum + offset */
expr = fold_build_pointer_plus_loc (input_location, expr, offset);
/* (ftype*)((char*)datum + offset) */
expr = build_c_cast (input_location, build_pointer_type (ftype), expr);
/* Finally: *(ftype*)((char*)datum + offset) */
ref = build_indirect_ref (input_location, expr, RO_UNARY_STAR);
/* We must set type of the resulting expression to be the same as
the field type. This is because, build_indirect_ref (...)
rebuilds the type which may result in lost information; as in the
case of protocol-qualified types (id <protocol> ). */
TREE_TYPE (ref) = ftype;
if (TREE_READONLY (datum) || TREE_READONLY (field))
TREE_READONLY (ref) = 1;
if (TREE_THIS_VOLATILE (datum) || TREE_THIS_VOLATILE (field))
TREE_THIS_VOLATILE (ref) = 1;
if (TREE_DEPRECATED (field))
warn_deprecated_use (field, NULL_TREE);
return ref;
}
/* IVAR refs are made via an externally referenceable offset and built
on the fly. That is, unless they refer to (private) fields in the
class structure. */
static tree
next_runtime_abi_02_build_ivar_ref (location_t loc ATTRIBUTE_UNUSED,
tree base, tree id)
{
tree ivar;
if ((ivar = objc_v2_build_ivar_ref (base, id)))
return ivar;
return objc_build_component_ref (base, id);
}
/* [super ...] references are listed here (and built into a table at
meta -data emit time). */
static tree
build_v2_superclass_ref_decl (tree ident, bool inst)
{
tree decl;
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "_OBJC_%sSuperRef_%s", (inst?"":"Meta"),
IDENTIFIER_POINTER (ident));
decl = start_var_decl (objc_class_type, buf);
OBJCMETA (decl, objc_meta, meta_superclass_ref);
return decl;
}
static GTY (()) vec<ident_data_tuple, va_gc> *class_super_refs;
static GTY (()) vec<ident_data_tuple, va_gc> *metaclass_super_refs;
/* Find or build a superclass reference decl for class NAME. */
static tree
objc_get_superclass_ref_decl (tree name, bool inst_meth)
{
tree decl;
vec<ident_data_tuple, va_gc> *list = inst_meth ? class_super_refs
: metaclass_super_refs;
if (list)
{
int count;
ident_data_tuple *ref;
FOR_EACH_VEC_ELT (*list, count, ref)
{
if (ref->ident == name)
{
if (!ref->data)
ref->data = build_v2_superclass_ref_decl (name, inst_meth);
return ref->data;
}
}
}
else
{
/* Somewhat arbitrary initial provision. */
if (inst_meth)
{
vec_alloc (class_super_refs, 16);
list = class_super_refs;
}
else
{
vec_alloc (metaclass_super_refs, 16);
list = metaclass_super_refs;
}
}
/* We come here if we don't find the entry - or if the table was yet
to be created. */
decl = build_v2_superclass_ref_decl (name, inst_meth);
ident_data_tuple e;
e.ident = name;
e.data = decl;
vec_safe_push (list, e);
return decl;
}
/* Get a reference to the superclass for IMP. */
static tree
next_runtime_abi_02_get_class_super_ref (location_t loc ATTRIBUTE_UNUSED,
struct imp_entry *imp, bool inst_meth)
{
tree name = CLASS_NAME (imp->imp_context);
return objc_get_superclass_ref_decl (name, inst_meth);
}
/* Get a reference to the superclass for category IMP. */
static tree
next_runtime_abi_02_get_category_super_ref (location_t loc ATTRIBUTE_UNUSED,
struct imp_entry *imp,
bool inst_meth)
{
if (flag_zero_link)
{
/* Do it the slow way. */
tree get_cl_fn = inst_meth ? objc_get_class_decl
: objc_get_meta_class_decl;
tree super_name = CLASS_SUPER_NAME (imp->imp_template);
super_name = my_build_string_pointer (IDENTIFIER_LENGTH (super_name) + 1,
IDENTIFIER_POINTER (super_name));
/* super_class = objc_get{Meta}Class("CLASS_SUPER_NAME"); */
return build_function_call (input_location, get_cl_fn,
build_tree_list (NULL_TREE, super_name));
}
/* This is the 'usual' path. */
tree cls_name = CLASS_NAME (imp->imp_template);
if (!inst_meth)
return objc_get_superclass_ref_decl (cls_name, inst_meth);
return objc_get_class_reference (cls_name);
}
static tree
next_runtime_abi_02_receiver_is_class_object (tree receiver)
{
if (TREE_CODE (receiver) == VAR_DECL
&& IS_CLASS (TREE_TYPE (receiver))
&& vec_safe_length (classrefs))
{
int count;
ident_data_tuple *ref;
/* The receiver is a variable created by build_class_reference_decl. */
FOR_EACH_VEC_ELT (*classrefs, count, ref)
if (ref->data == receiver)
return ref->ident;
}
return NULL_TREE;
}
/* Assign all arguments in VALUES which have side-effect to a temporary
and replaced that argument in VALUES list with the temporary. The
arguments will be passed to a function with FNTYPE. */
static tree
objc_copy_to_temp_side_effect_params (tree fntype, tree values)
{
tree valtail;
function_args_iterator iter;
/* Skip over receiver and the &_msf_ref types. */
function_args_iter_init (&iter, fntype);
function_args_iter_next (&iter);
function_args_iter_next (&iter);
for (valtail = values; valtail;
valtail = TREE_CHAIN (valtail), function_args_iter_next (&iter))
{
tree value = TREE_VALUE (valtail);
tree type = function_args_iter_cond (&iter);
if (type == NULL_TREE)
break;
if (!TREE_SIDE_EFFECTS (value))
continue;
/* To prevent re-evaluation. */
value = save_expr (value);
add_stmt (value);
TREE_VALUE (valtail) = value;
}
return values;
}
/* Build the new abi's messaging library call. It looks like:
(*_msg.messenger) (receiver, &_msg, ...) */
static tree
build_v2_objc_method_fixup_call (int super_flag, tree method_prototype,
tree lookup_object, tree selector,
tree method_params, bool check_for_nil)
{
tree ret_val;
tree sender, rcv_p, t;
tree ret_type
= (method_prototype
? TREE_VALUE (TREE_TYPE (method_prototype))
: objc_object_type);
tree ftype = build_function_type_for_method (ret_type, method_prototype,
METHOD_REF, super_flag);
tree sender_cast;
if (method_prototype && METHOD_TYPE_ATTRIBUTES (method_prototype))
ftype = build_type_attribute_variant (
ftype, METHOD_TYPE_ATTRIBUTES (method_prototype));
sender_cast = build_pointer_type (ftype);
if (check_for_nil)
method_params = objc_copy_to_temp_side_effect_params (ftype,
method_params);
/* Get &message_ref_t.messenger. */
sender = build_c_cast (input_location,
build_pointer_type (super_flag
? objc_v2_super_imp_type
: objc_v2_imp_type),
selector);
sender = build_indirect_ref (input_location, sender, RO_UNARY_STAR);
rcv_p = (super_flag ? objc_super_type : objc_object_type);
lookup_object = build_c_cast (input_location, rcv_p, lookup_object);
/* Use SAVE_EXPR to avoid evaluating the receiver twice. */
lookup_object = save_expr (lookup_object);
method_params = tree_cons (NULL_TREE, lookup_object,
tree_cons (NULL_TREE, selector,
method_params));
t = build3 (OBJ_TYPE_REF, sender_cast, sender, lookup_object, size_zero_node);
ret_val = build_function_call (input_location, t, method_params);
if (check_for_nil)
{
/* receiver != nil ? ret_val : 0 */
tree ftree;
tree ifexp;
if (TREE_CODE (ret_type) == RECORD_TYPE
|| TREE_CODE (ret_type) == UNION_TYPE)
/* An empty constructor is zero-filled by the middle end. */
ftree = objc_build_constructor (ret_type, NULL);
else
ftree = fold_convert (ret_type, integer_zero_node);
ifexp = build_binary_op (input_location, NE_EXPR,
lookup_object,
fold_convert (rcv_p, integer_zero_node), 1);
#ifdef OBJCPLUS
ret_val = build_conditional_expr (input_location,
ifexp, ret_val, ftree,
tf_warning_or_error);
#else
ret_val = build_conditional_expr (input_location,
ifexp, 0,
ret_val, NULL_TREE, input_location,
ftree, NULL_TREE, input_location);
ret_val = fold_convert (ret_type, ret_val);
#endif
}
return ret_val;
}
static tree
build_v2_build_objc_method_call (int super, tree method_prototype,
tree lookup_object, tree selector,
tree method_params, location_t loc,
bool check_for_nil, bool rx_is_id)
{
tree sender, sender_cast, method, t;
tree rcv_p = (super ? objc_super_type : objc_object_type);
vec<tree, va_gc> *parms;
unsigned nparm = (method_params ? list_length (method_params) : 0);
/* If a prototype for the method to be called exists, then cast
the sender's return type and arguments to match that of the method.
Otherwise, leave sender as is. */
tree ret_type
= (method_prototype
? TREE_VALUE (TREE_TYPE (method_prototype))
: objc_object_type);
tree ftype = build_function_type_for_method (ret_type, method_prototype,
METHOD_REF, super);
if (method_prototype && METHOD_TYPE_ATTRIBUTES (method_prototype))
ftype = build_type_attribute_variant (ftype,
METHOD_TYPE_ATTRIBUTES
(method_prototype));
sender_cast = build_pointer_type (ftype);
lookup_object = build_c_cast (loc, rcv_p, lookup_object);
/* Use SAVE_EXPR to avoid evaluating the receiver twice. */
lookup_object = save_expr (lookup_object);
/* Param list + 2 slots for object and selector. */
vec_alloc (parms, nparm + 2);
/* If we are returning an item that must be returned in memory, and the
target ABI does this by an invisible pointer provided as the first arg,
we need to adjust the message signature to include this. The second
part of this excludes targets that provide some alternate scheme for
structure returns. */
if (ret_type && !VOID_TYPE_P (ret_type)
&& targetm.calls.return_in_memory (ret_type, 0)
&& !(targetm.calls.struct_value_rtx (0, 0)
&& (TREE_CODE (ret_type) == RECORD_TYPE
|| TREE_CODE (ret_type) == UNION_TYPE)))
{
if (super)
sender = umsg_id_super2_stret_fixup_decl;
else
sender = rx_is_id ? umsg_id_stret_fixup_decl
: umsg_stret_fixup_decl;
}
else
{
if (super)
sender = umsg_id_super2_fixup_decl;
else
sender = rx_is_id ? umsg_id_fixup_decl
: umsg_fixup_decl;
}
method = build_fold_addr_expr_loc (loc, sender);
/* Pass the object to the method. */
parms->quick_push (lookup_object);
/* Pass the selector to the method. */
parms->quick_push (selector);
/* Now append the remainder of the parms. */
if (nparm)
for (; method_params; method_params = TREE_CHAIN (method_params))
parms->quick_push (TREE_VALUE (method_params));
/* Build an obj_type_ref, with the correct cast for the method call. */
t = build3 (OBJ_TYPE_REF, sender_cast, method,
lookup_object, size_zero_node);
tree ret_val = build_function_call_vec (loc, vNULL, t, parms, NULL);
vec_free (parms);
if (check_for_nil)
{
/* receiver != nil ? ret_val : 0 */
tree ftree;
tree ifexp;
if (TREE_CODE (ret_type) == RECORD_TYPE
|| TREE_CODE (ret_type) == UNION_TYPE)
{
/* An empty constructor is zero-filled by the middle end. */
ftree = objc_build_constructor (ret_type, NULL);
}
else
ftree = fold_convert (ret_type, integer_zero_node);
ifexp = build_binary_op (loc, NE_EXPR,
lookup_object,
fold_convert (rcv_p, integer_zero_node), 1);
#ifdef OBJCPLUS
ret_val = build_conditional_expr (loc, ifexp, ret_val, ftree,
tf_warning_or_error);
#else
ret_val = build_conditional_expr (loc, ifexp, 1,
ret_val, NULL_TREE, loc,
ftree, NULL_TREE, loc);
ret_val = fold_convert (ret_type, ret_val);
#endif
}
return ret_val;
}
static tree
next_runtime_abi_02_build_objc_method_call (location_t loc,
tree method_prototype,
tree receiver,
tree rtype,
tree sel_name,
tree method_params,
int super)
{
/* Do we need to check for nil receivers ? */
/* For now, message sent to classes need no nil check. In the
future, class declaration marked as weak_import must be nil
checked. */
bool check_for_nil = flag_objc_nilcheck;
if (super
|| (TREE_CODE (receiver) == VAR_DECL
&& TREE_TYPE (receiver) == objc_class_type))
check_for_nil = false;
if (flag_next_runtime >= USE_FIXUP_BEFORE)
{
tree selector
= next_runtime_abi_02_build_selector_reference (loc, sel_name,
method_prototype);
return build_v2_build_objc_method_call (super, method_prototype,
receiver, selector,
method_params, loc,
check_for_nil,
objc_is_id (rtype));
}
/* else we have to build a pair of the function and selector. */
tree message_func_decl;
tree ret_type = method_prototype
? TREE_VALUE (TREE_TYPE (method_prototype))
: objc_object_type;
/* See comment for the fixup version above. */
if (ret_type && !VOID_TYPE_P (ret_type)
&& targetm.calls.return_in_memory (ret_type, 0)
&& !(targetm.calls.struct_value_rtx (0, 0)
&& (TREE_CODE (ret_type) == RECORD_TYPE
|| TREE_CODE (ret_type) == UNION_TYPE)))
{
if (super)
message_func_decl = umsg_id_super2_stret_fixup_decl;
else
message_func_decl = objc_is_id (rtype)
? umsg_id_stret_fixup_decl
: umsg_stret_fixup_decl;
}
else
{
if (super)
message_func_decl = umsg_id_super2_fixup_decl;
else
message_func_decl = objc_is_id (rtype)
? umsg_id_fixup_decl
: umsg_fixup_decl;
}
tree selector = build_v2_selector_messenger_reference (sel_name,
message_func_decl);
/* selector = &_msg; */
selector = build_unary_op (loc, ADDR_EXPR, selector, 0);
selector = build_c_cast (loc, (super ? objc_v2_super_selector_type
: objc_v2_selector_type),
selector);
/* (*_msg.messenger) (receiver, &_msg, ...); */
return build_v2_objc_method_fixup_call (super, method_prototype, receiver,
selector, method_params,
check_for_nil);
}
/* NOTE --- Constant String Class Stuff --- */
static bool
next_runtime_abi_02_setup_const_string_class_decl (void)
{
if (!constant_string_global_id)
{
/* Hopefully, this should not represent a serious limitation. */
char buf[BUFSIZE];
snprintf (buf, BUFSIZE, "OBJC_CLASS_$_%s", constant_string_class_name);
constant_string_global_id = get_identifier (buf);
}
string_class_decl = lookup_name (constant_string_global_id);
/* In OBJC2 abi, constant string class reference refers to class
name for NSConstantString class. This declaration may not be
available yet (in fact it is not in most cases). So, declare an
extern OBJC_CLASS_$_NSConstantString in its place. */
if (!string_class_decl)
string_class_decl =
create_extern_decl (objc_v2_class_template,
IDENTIFIER_POINTER (constant_string_global_id));
return (string_class_decl != NULL_TREE);
}
static tree
next_runtime_abi_02_build_const_string_constructor (location_t loc, tree string,
int length)
{
tree constructor, fields, var;
vec<constructor_elt, va_gc> *v = NULL;
/* NeXT: (NSConstantString *) & ((__builtin_ObjCString) { isa, string, length }) */
fields = TYPE_FIELDS (internal_const_str_type);
CONSTRUCTOR_APPEND_ELT (v, fields,
build_unary_op (loc, ADDR_EXPR, string_class_decl, 0));
fields = DECL_CHAIN (fields);
CONSTRUCTOR_APPEND_ELT (v, fields,
build_unary_op (loc, ADDR_EXPR, string, 1));
/* ??? check if this should be long. */
fields = DECL_CHAIN (fields);
CONSTRUCTOR_APPEND_ELT (v, fields, build_int_cst (NULL_TREE, length));
constructor = objc_build_constructor (internal_const_str_type, v);
var = build_decl (input_location, CONST_DECL, NULL, TREE_TYPE (constructor));
DECL_INITIAL (var) = constructor;
TREE_STATIC (var) = 1;
DECL_CONTEXT (var) = NULL;
OBJCMETA (var, objc_meta, meta_const_str);
return var;
}
/* NOTE --- NeXT V2 Metadata templates --- */
/* This routine builds the following type:
struct _prop_t
{
const char * const name; // property name
const char * const attributes; // comma-delimited, encoded,
// property attributes
};
*/
static tree
build_v2_property_template (void)
{
tree prop_record;
tree decls, *chain = NULL;
prop_record = objc_start_struct (get_identifier ("_prop_t"));
/* const char * name */
decls = add_field_decl (string_type_node, "name", &chain);
/* const char * attribute */
add_field_decl (string_type_node, "attribute", &chain);
objc_finish_struct (prop_record, decls);
return prop_record;
}
/* struct ivar_t
{
unsigned long int *offset;
char *name;
char *type;
uint32_t alignment;
uint32_t size;
};
*/
static tree
build_v2_ivar_t_template (void)
{
tree objc_ivar_id, objc_ivar_record;
tree decls, *chain = NULL;
objc_ivar_id = get_identifier ("_ivar_t");
objc_ivar_record = objc_start_struct (objc_ivar_id);
/* unsigned long int *offset; */
decls = add_field_decl (build_pointer_type
(TREE_TYPE (size_zero_node)), "offset", &chain);
/* char *name; */
add_field_decl (string_type_node, "name", &chain);
/* char *type; */
add_field_decl (string_type_node, "type", &chain);
/* uint32_t alignment; */
add_field_decl (integer_type_node, "alignment", &chain);
/* uint32_t size; */
add_field_decl (integer_type_node, "size", &chain);
objc_finish_struct (objc_ivar_record, decls);
return objc_ivar_record;
}
static void
build_metadata_templates (void)
{
if (!objc_method_template)
objc_method_template = build_method_template ();
if (!objc_v2_property_template)
objc_v2_property_template = build_v2_property_template ();
if (!objc_v2_ivar_template)
objc_v2_ivar_template = build_v2_ivar_t_template ();
}
/* NOTE --- Output NeXT V2 Metadata --- */
/* Routine builds name of Interface's main meta-data of type class_t. */
static char *
objc_build_internal_classname (tree ident, bool metaclass)
{
static char string[512];
snprintf (string, 512, "%s_%s", metaclass ? "OBJC_METACLASS_$"
: "OBJC_CLASS_$",
IDENTIFIER_POINTER (ident));
return string;
}
/* Build the name for object of type struct class_ro_t */
static const char *
newabi_append_ro (const char *name)
{
const char *dollar;
char *p;
static char string[BUFSIZE];
dollar = strchr (name, '$');
gcc_assert (dollar);
p = string;
*p = '_'; p++;
strncpy (p, name, (int)(dollar - name));
p += (int)(dollar - name);
sprintf (p, "RO_%s", dollar);
return string;
}
/* Build the struct message_ref_t msg =
{objc_msgSend_fixup_xxx, @selector(func)}
table. */
static
void build_v2_message_ref_translation_table (void)
{
int count;
msgref_entry *ref;
if (!vec_safe_length (msgrefs))
return;
FOR_EACH_VEC_ELT (*msgrefs, count, ref)
{
vec<constructor_elt, va_gc> *initializer;
tree expr, constructor;
tree struct_type = TREE_TYPE (ref->refdecl);
location_t loc = DECL_SOURCE_LOCATION (ref->refdecl);
initializer = NULL;
/* First 'IMP messenger' field... */
expr = build_unary_op (loc, ADDR_EXPR, ref->func, 0);
expr = convert (objc_v2_imp_type, expr);
CONSTRUCTOR_APPEND_ELT (initializer, NULL_TREE, expr);
/* ... then 'SEL name' field. */
expr = build_selector (ref->selname);
CONSTRUCTOR_APPEND_ELT (initializer, NULL_TREE, expr);
constructor = objc_build_constructor (struct_type, initializer);
finish_var_decl (ref->refdecl, constructor);
}
}
/* Build decl = initializer; for each externally visible class
reference. */
static void
build_v2_classrefs_table (void)
{
int count;
ident_data_tuple *ref;
if (!vec_safe_length (classrefs))
return;
FOR_EACH_VEC_ELT (*classrefs, count, ref)
{
tree expr = ref->ident;
tree decl = ref->data;
/* Interface with no implementation and yet one of its messages
has been used. Need to generate a full address-of tree for it
here. */
if (TREE_CODE (expr) == IDENTIFIER_NODE)
{
const char *name = objc_build_internal_classname (expr, false);
expr = create_extern_decl (objc_v2_class_template, name);
expr = convert (objc_class_type, build_fold_addr_expr (expr));
}
/* The runtime wants this, even if it appears unused, so we must force the
output.
DECL_PRESERVE_P (decl) = 1; */
finish_var_decl (decl, expr);
}
}
/* Build decl = initializer; for each externally visible super class
reference. */
static void
build_v2_super_classrefs_table (bool metaclass)
{
int count;
ident_data_tuple *ref;
vec<ident_data_tuple, va_gc> *list = metaclass ? metaclass_super_refs
: class_super_refs;
if (!vec_safe_length (list))
return;
FOR_EACH_VEC_ELT (*list, count, ref)
{
tree expr = ref->ident;
tree decl = ref->data;
/* Interface with no implementation and yet one of its messages
has been used. Need to generate a full address-of tree for it
here. */
if (TREE_CODE (expr) == IDENTIFIER_NODE)
{
const char * name = objc_build_internal_classname (expr, metaclass);
expr = create_extern_decl (objc_v2_class_template, name);
expr = convert (objc_class_type, build_fold_addr_expr (expr));
}
finish_var_decl (decl, expr);
}
}
/* Add the global class meta-data declaration to the list which later
on ends up in the __class_list section. */
static GTY(()) vec<tree, va_gc> *class_list;
static void
objc_v2_add_to_class_list (tree global_class_decl)
{
vec_safe_push (class_list, global_class_decl);
}
static GTY(()) vec<tree, va_gc> *nonlazy_class_list;
/* Add the global class meta-data declaration to the list which later
on ends up in the __nonlazy_class section. */
static void
objc_v2_add_to_nonlazy_class_list (tree global_class_decl)
{
vec_safe_push (nonlazy_class_list, global_class_decl);
}
static GTY(()) vec<tree, va_gc> *category_list;
/* Add the category meta-data declaration to the list which later on
ends up in the __nonlazy_category section. */
static void
objc_v2_add_to_category_list (tree decl)
{
vec_safe_push (category_list, decl);
}
static GTY(()) vec<tree, va_gc> *nonlazy_category_list;
/* Add the category meta-data declaration to the list which later on
ends up in the __category_list section. */
static void
objc_v2_add_to_nonlazy_category_list (tree decl)
{
vec_safe_push (nonlazy_category_list, decl);
}
static bool
has_load_impl (tree clsmeth)
{
while (clsmeth)
{
tree id = METHOD_SEL_NAME (clsmeth);
if (IDENTIFIER_LENGTH (id) == 4
&& startswith (IDENTIFIER_POINTER (id), "load"))
return true;
clsmeth = DECL_CHAIN (clsmeth);
}
return false;
}
/* Build a __{class,category}_list section table containing address of
all @implemented {class,category} meta-data. */
static void
build_v2_address_table (vec<tree, va_gc> *src, const char *nam, tree attr)
{
int count=0;
tree type, decl, expr;
vec<constructor_elt, va_gc> *initlist = NULL;
if (!vec_safe_length (src))
return;
FOR_EACH_VEC_ELT (*src, count, decl)
{
#ifndef OBJCPLUS
tree purpose = build_int_cst (NULL_TREE, count);
#else
tree purpose = NULL_TREE;
#endif
expr = convert (objc_class_type, build_fold_addr_expr (decl));
CONSTRUCTOR_APPEND_ELT (initlist, purpose, expr);
}
gcc_assert (count > 0);
type = build_array_type (objc_class_type,
build_index_type (build_int_cst (NULL_TREE, count - 1)));
decl = start_var_decl (type, nam);
/* The runtime wants this, even if it appears unused, so we must
force the output. */
DECL_PRESERVE_P (decl) = 1;
expr = objc_build_constructor (type, initlist);
OBJCMETA (decl, objc_meta, attr);
finish_var_decl (decl, expr);
}
/* Build decl = initializer; for each protocol referenced in
@protocol(MyProt) expression. Refs as built in the entry section
above. */
static void
build_v2_protocol_list_translation_table (void)
{
int count;
prot_list_entry *ref;
if (!protrefs)
return;
FOR_EACH_VEC_ELT (*protrefs, count, ref)
{
char buf[BUFSIZE];
tree expr;
gcc_assert (TREE_CODE (ref->id) == PROTOCOL_INTERFACE_TYPE);
snprintf (buf, BUFSIZE, "_OBJC_Protocol_%s",
IDENTIFIER_POINTER (PROTOCOL_NAME (ref->id)));
expr = start_var_decl (objc_v2_protocol_template, buf);
expr = convert (objc_protocol_type, build_fold_addr_expr (expr));
finish_var_decl (ref->refdecl, expr);
}
/* TODO: Maybe we could explicitly delete the vec. now? */
}
static GTY (()) vec<prot_list_entry, va_gc> *protlist;
/* Add the local protocol meta-data declaration to the list which
later on ends up in the __protocol_list section. */
static void
objc_add_to_protocol_list (tree protocol_interface_decl, tree protocol_decl)
{
prot_list_entry e;
if (!protlist)
/* Arbitrary init count. */
vec_alloc (protlist, 32);
e.id = protocol_interface_decl;
e.refdecl = protocol_decl;
vec_safe_push (protlist, e);
}
/* Build the __protocol_list section table containing address of all
generate protocol_t meta-data. */
static void
build_v2_protocol_list_address_table (void)
{
int count;
prot_list_entry *ref;
if (!vec_safe_length (protlist))
return;
FOR_EACH_VEC_ELT (*protlist, count, ref)
{
tree decl, expr;
char buf[BUFSIZE];
gcc_assert (ref->id && TREE_CODE (ref->id) == PROTOCOL_INTERFACE_TYPE);
snprintf (buf, BUFSIZE, "_OBJC_LabelProtocol_%s",
IDENTIFIER_POINTER (PROTOCOL_NAME (ref->id)));
if (flag_next_runtime >= USE_FIXUP_BEFORE)
{
decl = create_hidden_decl (objc_protocol_type, buf, /*is def=*/true);
DECL_WEAK (decl) = true;
}
else
decl = create_global_decl (objc_protocol_type, buf, /*is def=*/true);
expr = convert (objc_protocol_type, build_fold_addr_expr (ref->refdecl));
OBJCMETA (decl, objc_meta, meta_label_protocollist);
finish_var_decl (decl, expr);
}
/* TODO: delete the vec. */
/* TODO: upgrade to the clang/llvm hidden version. */
}
/* This routine declares a variable to hold meta data for 'struct
protocol_list_t'. */
static tree
generate_v2_protocol_list (tree i_or_p, tree klass_ctxt)
{
tree refs_decl, lproto, e, plist, ptempl_p_t;
int size = 0;
vec<constructor_elt, va_gc> *initlist = NULL;
char buf[BUFSIZE];
if (TREE_CODE (i_or_p) == CLASS_INTERFACE_TYPE
|| TREE_CODE (i_or_p) == CATEGORY_INTERFACE_TYPE)
plist = CLASS_PROTOCOL_LIST (i_or_p);
else if (TREE_CODE (i_or_p) == PROTOCOL_INTERFACE_TYPE)
plist = PROTOCOL_LIST (i_or_p);
else
gcc_unreachable ();
/* Compute size. */
for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto))
if (TREE_CODE (TREE_VALUE (lproto)) == PROTOCOL_INTERFACE_TYPE
&& PROTOCOL_FORWARD_DECL (TREE_VALUE (lproto)))
size++;
/* Build initializer. */
ptempl_p_t = build_pointer_type (objc_v2_protocol_template);
e = build_int_cst (ptempl_p_t, size);
CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, e);
for (lproto = plist; lproto; lproto = TREE_CHAIN (lproto))
{
tree pval = TREE_VALUE (lproto);
if (TREE_CODE (pval) == PROTOCOL_INTERFACE_TYPE
&& PROTOCOL_FORWARD_DECL (pval))
{
tree fwref = PROTOCOL_FORWARD_DECL (pval);
location_t loc = DECL_SOURCE_LOCATION (fwref) ;
e = build_unary_op (loc, ADDR_EXPR, fwref, 0);
CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE, e);
}
}
/* static struct protocol_list_t *list[size]; */
switch (TREE_CODE (i_or_p))
{
case PROTOCOL_INTERFACE_TYPE:
snprintf (buf, BUFSIZE, "_OBJC_ProtocolRefs_%s",
IDENTIFIER_POINTER (PROTOCOL_NAME (i_or_p)));
break;
case CLASS_INTERFACE_TYPE:
snprintf (buf, BUFSIZE, "_OBJC_ClassProtocols_%s",
IDENTIFIER_POINTER (CLASS_NAME (i_or_p)));
break;
case CATEGORY_INTERFACE_TYPE:
snprintf (buf, BUFSIZE, "_OBJC_CategoryProtocols_%s_%s",
IDENTIFIER_POINTER (CLASS_NAME (klass_ctxt)),
IDENTIFIER_POINTER (CLASS_SUPER_NAME (klass_ctxt)));
break;
default:
gcc_unreachable ();
}
refs_decl = start_var_decl (build_sized_array_type (ptempl_p_t, size+1),
buf);
/* ObjC2 puts all these in the base section. */
OBJCMETA (refs_decl, objc_meta, meta_base);
DECL_PRESERVE_P (refs_decl) = 1;
finish_var_decl (refs_decl,
objc_build_constructor (TREE_TYPE (refs_decl),initlist));
return refs_decl;
}
/* This routine builds one 'struct method_t' initializer list. Note
that the old ABI is supposed to build 'struct objc_method' which
has 3 fields, but it does not build the initialization expression
for 'method_imp' which for protocols is NULL any way. To be
consistent with declaration of 'struct method_t', in the new ABI we
set the method_t.imp to NULL. */
static tree
build_v2_descriptor_table_initializer (tree type, tree entries)
{
vec<constructor_elt, va_gc> *initlist = NULL;
do
{
vec<constructor_elt, va_gc> *eltlist = NULL;
CONSTRUCTOR_APPEND_ELT (eltlist, NULL_TREE,
build_selector (METHOD_SEL_NAME (entries)));
CONSTRUCTOR_APPEND_ELT (eltlist, NULL_TREE,
add_objc_string (METHOD_ENCODING (entries),
meth_var_types));
CONSTRUCTOR_APPEND_ELT (eltlist, NULL_TREE, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (initlist, NULL_TREE,
objc_build_constructor (type, eltlist));
entries = TREE_CHAIN (entries);
}
while (entries);
return objc_build_constructor (build_array_type (type, 0), initlist);
}
/* struct method_list_t
{
uint32_t entsize;
uint32_t method_count;
struct objc_method method_list[method_count];
}; */
static tree
build_v2_method_list_template (tree list_type, int size)
{
tree method_list_t_record;
tree array_type, decls, *chain = NULL;
method_list_t_record = objc_start_struct (NULL_TREE);
/* uint32_t const entsize; */
decls = add_field_decl (integer_type_node, "entsize", &chain);
/* int method_count; */
add_field_decl (integer_type_node, "method_count", &chain);
/* struct objc_method method_list[]; */
array_type = build_sized_array_type (list_type, size);
add_field_decl (array_type, "method_list", &chain);
objc_finish_struct (method_list_t_record, decls);
return method_list_t_record;
}
/* Note, as above that we are building to the objc_method_template
which has the *imp field. ABI0/1 build with
objc_method_prototype_template which is missing this field. */
static tree
generate_v2_meth_descriptor_table (tree chain, tree protocol,
const char *prefix, tree attr,
vec<tree>& all_meths)
{
tree method_list_template, initlist, decl;
int size, entsize;
vec<constructor_elt, va_gc> *v = NULL;
char buf[BUFSIZE];
if (!chain || !prefix)
return NULL_TREE;
tree method = chain;
size = 0;
while (method)
{
if (! METHOD_ENCODING (method))
METHOD_ENCODING (method) = encode_method_prototype (method);
all_meths.safe_push (method);
method = TREE_CHAIN (method);
size++;
}
gcc_assert (size);
method_list_template = build_v2_method_list_template (objc_method_template,
size);
snprintf (buf, BUFSIZE, "%s_%s", prefix,
IDENTIFIER_POINTER (PROTOCOL_NAME (protocol)));
decl = start_var_decl (method_list_template, buf);
entsize = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_method_template));
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, entsize));
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, size));
initlist =
build_v2_descriptor_table_initializer (objc_method_template,
chain);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, initlist);
/* Get into the right section. */
OBJCMETA (decl, objc_meta, attr);
finish_var_decl (decl, objc_build_constructor (method_list_template, v));
return decl;
}
static tree
generate_v2_meth_type_list (vec<tree>& all_meths, tree protocol,
const char *prefix)
{
if (all_meths.is_empty () || !prefix)
return NULL_TREE;
unsigned size = all_meths.length ();
tree list_type = build_sized_array_type (string_type_node, size);
char *nam;
asprintf (&nam, "%s_%s", prefix,
IDENTIFIER_POINTER (PROTOCOL_NAME (protocol)));
tree decl = start_var_decl (list_type, nam);
free (nam);
OBJCMETA (decl, objc_meta, meta_base);
vec<constructor_elt, va_gc> *v = NULL;
for (unsigned i = 0; i < size; ++i)
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
add_objc_string (METHOD_ENCODING (all_meths[i]),
meth_var_types));
finish_var_decl (decl, objc_build_constructor (list_type, v));
return decl;
}
/* This routine builds the initializer list to initialize the 'struct
_prop_t prop_list[]' field of 'struct _prop_list_t' meta-data. */
static tree
build_v2_property_table_initializer (tree type, tree context)
{
tree x;
vec<constructor_elt, va_gc> *inits = NULL;
if (TREE_CODE (context) == PROTOCOL_INTERFACE_TYPE)
x = CLASS_PROPERTY_DECL (context);
else
x = IMPL_PROPERTY_DECL (context);
for (; x; x = TREE_CHAIN (x))
{
vec<constructor_elt, va_gc> *elemlist = NULL;
/* NOTE! sections where property name/attribute go MUST change
later. */
tree attribute, name_ident = PROPERTY_NAME (x);
CONSTRUCTOR_APPEND_ELT (elemlist, NULL_TREE,
add_objc_string (name_ident, prop_names_attr));
attribute = objc_v2_encode_prop_attr (x);
CONSTRUCTOR_APPEND_ELT (elemlist, NULL_TREE,
add_objc_string (attribute, prop_names_attr));
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE,
objc_build_constructor (type, elemlist));
}
return objc_build_constructor (build_array_type (type, 0),inits);
}
/* This routine builds the following type:
struct _prop_list_t
{
uint32_t entsize; // sizeof (struct _prop_t)
uint32_t prop_count;
struct _prop_t prop_list [prop_count];
}
*/
static tree
build_v2_property_list_template (tree list_type, int size)
{
tree property_list_t_record;
tree array_type, decls, *chain = NULL;
/* anonymous. */
property_list_t_record = objc_start_struct (NULL_TREE);
/* uint32_t const entsize; */
decls = add_field_decl (integer_type_node, "entsize", &chain);
/* int prop_count; */
add_field_decl (integer_type_node, "prop_count", &chain);
/* struct _prop_t prop_list[]; */
array_type = build_sized_array_type (list_type, size);
add_field_decl (array_type, "prop_list", &chain);
objc_finish_struct (property_list_t_record, decls);
return property_list_t_record;
}
/* Top-level routine to generate property tables for each
implementation. */
static tree
generate_v2_property_table (tree context, tree klass_ctxt)
{
tree x, decl, initlist, property_list_template;
bool is_proto = false;
vec<constructor_elt, va_gc> *inits = NULL;
int init_val, size = 0;
char buf[BUFSIZE];
if (context)
{
gcc_assert (TREE_CODE (context) == PROTOCOL_INTERFACE_TYPE);
x = CLASS_PROPERTY_DECL (context);
is_proto = true;
}
else
x = IMPL_PROPERTY_DECL (klass_ctxt);
for (; x; x = TREE_CHAIN (x))
size++;
if (size == 0)
return NULL_TREE;
property_list_template =
build_v2_property_list_template (objc_v2_property_template,
size);
initlist = build_v2_property_table_initializer (objc_v2_property_template,
is_proto ? context
: klass_ctxt);
init_val = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_v2_property_template));
if (is_proto)
snprintf (buf, BUFSIZE, "_OBJC_ProtocolPropList_%s",
IDENTIFIER_POINTER (PROTOCOL_NAME (context)));
else
snprintf (buf, BUFSIZE, "_OBJC_ClassPropList_%s",
IDENTIFIER_POINTER (CLASS_NAME (klass_ctxt)));
decl = start_var_decl (property_list_template, buf);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE,
build_int_cst (NULL_TREE, init_val));
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE,
build_int_cst (NULL_TREE, size));
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, initlist);
OBJCMETA (decl, objc_meta, meta_base);
finish_var_decl (decl, objc_build_constructor (TREE_TYPE (decl), inits));
return decl;
}
static tree
build_v2_protocol_initializer (tree type, tree protocol_name, tree protocol_list,
tree inst_methods, tree class_methods,
tree opt_ins_meth, tree opt_cls_meth,
tree property_list, tree ext_meth_types,
tree demangled_name, tree class_prop_list)
{
tree expr, ttyp;
location_t loc;
vec<constructor_elt, va_gc> *inits = NULL;
/* TODO: find a better representation of location from the inputs. */
loc = UNKNOWN_LOCATION;
/* This is NULL for the new ABI. */
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE,
convert (objc_object_type, null_pointer_node));
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, protocol_name);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, protocol_list);
ttyp = objc_method_proto_list_ptr;
if (inst_methods)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, inst_methods, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
if (class_methods)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, class_methods, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
if (opt_ins_meth)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, opt_ins_meth, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
if (opt_cls_meth)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, opt_cls_meth, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
ttyp = objc_prop_list_ptr;
if (property_list)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, property_list, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
/* const uint32_t size; = sizeof(struct protocol_t) */
expr = build_int_cst (integer_type_node,
TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_v2_protocol_template)));
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
/* const uint32_t flags; = 0 */
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, integer_zero_node);
ttyp = build_pointer_type (string_type_node);
if (ext_meth_types)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, ext_meth_types, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
ttyp = string_type_node;
if (demangled_name)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, demangled_name, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
ttyp = objc_prop_list_ptr;
if (class_prop_list)
expr = convert (ttyp, build_unary_op (loc, ADDR_EXPR, class_prop_list, 0));
else
expr = convert (ttyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (inits, NULL_TREE, expr);
return objc_build_constructor (type, inits);
}
/* Main routine to build all meta data for all protocols used in a
translation unit. */
static void
generate_v2_protocols (void)
{
tree p ;
bool some = false;
if (!protocol_chain)
return ;
/* If a protocol was directly referenced, pull in indirect
references. */
for (p = protocol_chain; p; p = TREE_CHAIN (p))
if (PROTOCOL_FORWARD_DECL (p) && PROTOCOL_LIST (p))
generate_protocol_references (PROTOCOL_LIST (p));
for (p = protocol_chain; p; p = TREE_CHAIN (p))
{
location_t loc;
tree inst_meth, class_meth, opt_inst_meth, opt_class_meth, props;
tree decl, initlist, protocol_name_expr, refs_decl, refs_expr;
/* If protocol wasn't referenced, don't generate any code. */
decl = PROTOCOL_FORWARD_DECL (p);
if (!decl)
continue;
loc = DECL_SOURCE_LOCATION (decl);
some = true;
vec<tree> all_meths = vNULL;
inst_meth =
generate_v2_meth_descriptor_table (PROTOCOL_NST_METHODS (p), p,
"_OBJC_ProtocolInstanceMethods",
meta_proto_nst_meth, all_meths);
class_meth =
generate_v2_meth_descriptor_table (PROTOCOL_CLS_METHODS (p), p,
"_OBJC_ProtocolClassMethods",
meta_proto_cls_meth, all_meths);
opt_inst_meth =
generate_v2_meth_descriptor_table (PROTOCOL_OPTIONAL_NST_METHODS (p), p,
"_OBJC_ProtocolOptInstMethods",
meta_proto_nst_meth, all_meths);
opt_class_meth =
generate_v2_meth_descriptor_table (PROTOCOL_OPTIONAL_CLS_METHODS (p), p,
"_OBJC_ProtocolOptClassMethods",
meta_proto_cls_meth, all_meths);
if (PROTOCOL_LIST (p))
refs_decl = generate_v2_protocol_list (p, NULL_TREE);
else
refs_decl = 0;
/* static struct objc_protocol _OBJC_Protocol_<mumble>; */
protocol_name_expr = add_objc_string (PROTOCOL_NAME (p), class_names);
if (refs_decl)
refs_expr = convert (build_pointer_type (objc_v2_protocol_template),
build_unary_op (loc, ADDR_EXPR, refs_decl, 0));
else
refs_expr = build_int_cst (NULL_TREE, 0);
props = generate_v2_property_table (p, NULL_TREE);
tree ext_meth_types
= generate_v2_meth_type_list (all_meths, p,
"_OBJC_ProtocolMethodTypes");
tree demangled_name = NULL_TREE;
tree class_prop_list = NULL_TREE;
initlist = build_v2_protocol_initializer (TREE_TYPE (decl),
protocol_name_expr, refs_expr,
inst_meth, class_meth,
opt_inst_meth, opt_class_meth,
props, ext_meth_types,
demangled_name,class_prop_list);
finish_var_decl (decl, initlist);
objc_add_to_protocol_list (p, decl);
all_meths.truncate (0);
}
if (some)
{
/* Make sure we get the Protocol class linked in - reference
it... */
p = objc_v2_get_class_reference (get_identifier (PROTOCOL_OBJECT_CLASS_NAME));
/* ... but since we don't specifically use the reference... we
need to force it. */
DECL_PRESERVE_P (p) = 1;
}
}
static tree
generate_v2_dispatch_table (tree chain, const char *name, tree attr)
{
tree decl, method_list_template, initlist;
vec<constructor_elt, va_gc> *v = NULL;
int size, init_val;
if (!chain || !name || !(size = list_length (chain)))
return NULL_TREE;
method_list_template
= build_v2_method_list_template (objc_method_template, size);
initlist
= build_dispatch_table_initializer (objc_method_template, chain);
decl = start_var_decl (method_list_template, name);
init_val = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (objc_method_template));
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
build_int_cst (integer_type_node, init_val));
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
build_int_cst (integer_type_node, size));
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, initlist);
OBJCMETA (decl, objc_meta, attr);
finish_var_decl (decl,
objc_build_constructor (TREE_TYPE (decl), v));
return decl;
}
/* Init a category. */
static tree
build_v2_category_initializer (tree type, tree cat_name, tree class_name,
tree inst_methods, tree class_methods,
tree protocol_list, tree property_list,
location_t loc)
{
tree expr, ltyp;
vec<constructor_elt, va_gc> *v = NULL;
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, cat_name);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, class_name);
ltyp = objc_method_list_ptr;
if (inst_methods)
expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, inst_methods, 0));
else
expr = convert (ltyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr);
if (class_methods)
expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, class_methods, 0));
else
expr = convert (ltyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr);
/* protocol_list = */
ltyp = build_pointer_type (objc_v2_protocol_template);
if (protocol_list)
expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, protocol_list, 0));
else
expr = convert (ltyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr);
ltyp = objc_prop_list_ptr;
if (property_list)
expr = convert (ltyp, build_unary_op (loc, ADDR_EXPR, property_list, 0));
else
expr = convert (ltyp, null_pointer_node);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, expr);
return objc_build_constructor (type, v);
}
/* static struct category_t _OBJC_CATEGORY_$_<name> = { ... }; */
static void
generate_v2_category (struct imp_entry *impent)
{
tree initlist, cat_name_expr, class_name_expr;
tree protocol_decl, category, props, t;
tree inst_methods = NULL_TREE, class_methods = NULL_TREE;
tree cat = impent->imp_context;
tree cat_decl = impent->class_decl;
location_t loc;
char buf[BUFSIZE];
loc = DECL_SOURCE_LOCATION (cat_decl);
/* ??? not sure this is really necessary, the following references should
force appropriate linkage linkage...
-- but ... ensure a reference to the class... */
t = objc_v2_get_class_reference (CLASS_NAME (cat));
/* ... which we ignore so force it out.. */
DECL_PRESERVE_P (t) = 1;
snprintf (buf, BUFSIZE, "OBJC_CLASS_$_%s", IDENTIFIER_POINTER (CLASS_NAME (cat)));
class_name_expr = create_extern_decl (objc_v2_class_template, buf);
class_name_expr = build_fold_addr_expr (class_name_expr);
cat_name_expr = add_objc_string (CLASS_SUPER_NAME (cat), class_names);
category = lookup_category (impent->imp_template, CLASS_SUPER_NAME (cat));
if (category && CLASS_PROTOCOL_LIST (category))
{
generate_protocol_references (CLASS_PROTOCOL_LIST (category));
protocol_decl = generate_v2_protocol_list (category, cat);
}
else
protocol_decl = NULL_TREE;
/* decl = update_var_decl(impent->class_decl); */
props = generate_v2_property_table (NULL_TREE,