diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4cfc4c63f99..dcf090b32e2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,18 @@ +1998-08-28 Jason Merrill + + * search.c (dfs_search, binfo_for_vtable, dfs_bfv_helper): New fns. + * decl2.c (output_vtable_inherit): Call binfo_for_vtable. + +1998-08-28 Richard Henderson + + Add support for discarding unused virtual functions. + * lang-options.h: Add -fvtable-gc. + * cp-tree.h: Add flag_vtable_gc. + * decl2.c (output_vtable_inherit): New fn. + (finish_vtable_vardecl): Call it. + * class.c (build_vtable_entry_ref): New fn. + (build_vtbl_ref): Call it. + 1998-08-28 Mark Mitchell * cp-tree.h (build_enumerator): Take the enumeration type as a diff --git a/gcc/cp/class.c b/gcc/cp/class.c index da6c20ec9a2..dac59983d97 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -429,6 +429,36 @@ build_vtable_entry (delta, pfn) } } +/* We want to give the assembler the vtable identifier as well as + the offset to the function pointer. So we generate + + __asm__ __volatile__ (".vtable_entry %0, %1" + : : "s"(&class_vtable), + "i"((long)&vtbl[idx].pfn - (long)&vtbl[0])); */ + +static void +build_vtable_entry_ref (basetype, vtbl, idx) + tree basetype, vtbl, idx; +{ + static char asm_stmt[] = ".vtable_entry %0, %1"; + tree s, i, i2; + + s = build_unary_op (ADDR_EXPR, TYPE_BINFO_VTABLE (basetype), 0); + s = build_tree_list (build_string (1, "s"), s); + + i = build_array_ref (vtbl, idx); + if (!flag_vtable_thunks) + i = build_component_ref (i, pfn_identifier, vtable_entry_type, 0); + i = build_c_cast (ptrdiff_type_node, build_unary_op (ADDR_EXPR, i, 0)); + i2 = build_array_ref (vtbl, build_int_2(0,0)); + i2 = build_c_cast (ptrdiff_type_node, build_unary_op (ADDR_EXPR, i2, 0)); + i = build_binary_op (MINUS_EXPR, i, i2, 0); + i = build_tree_list (build_string (1, "i"), i); + + expand_asm_operands (build_string (sizeof(asm_stmt)-1, asm_stmt), + NULL_TREE, chainon (s, i), NULL_TREE, 1, NULL, 0); +} + /* Given an object INSTANCE, return an expression which yields the virtual function vtable element corresponding to INDEX. There are many special cases for INSTANCE which we take care of here, mainly @@ -489,7 +519,12 @@ build_vtbl_ref (instance, idx) vtbl = build_indirect_ref (build_vfield_ref (instance, basetype), NULL_PTR); } + assemble_external (vtbl); + + if (flag_vtable_gc) + build_vtable_entry_ref (basetype, vtbl, idx); + aref = build_array_ref (vtbl, idx); return aref; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f7ce4f2714e..010959d0b06 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -476,6 +476,9 @@ extern int flag_do_squangling; /* Nonzero if we want to issue diagnostics that the standard says are not required. */ extern int flag_optional_diags; + +/* Nonzero means output .vtable_{entry,inherit} for use in doing vtable gc. */ +extern int flag_vtable_gc; /* C++ language-specific tree codes. */ #define DEFTREECODE(SYM, NAME, TYPE, LENGTH) SYM, diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index ebd942dbde9..3e86fe6c638 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -454,6 +454,9 @@ int flag_guiding_decls; and class qualifiers. */ int flag_do_squangling; +/* Nonzero means output .vtable_{entry,inherit} for use in doing vtable gc. */ + +int flag_vtable_gc; /* Table of language-dependent -f options. STRING is the option name. VARIABLE is the address of the variable. @@ -496,6 +499,7 @@ static struct { char *string; int *variable; int on_value;} lang_f_options[] = {"init-priority", &flag_init_priority, 1}, {"huge-objects", &flag_huge_objects, 1}, {"conserve-space", &flag_conserve_space, 1}, + {"vtable-gc", &flag_vtable_gc, 1}, {"vtable-thunks", &flag_vtable_thunks, 1}, {"access-control", &flag_access_control, 1}, {"nonansi-builtins", &flag_no_nonansi_builtin, 0}, @@ -2672,7 +2676,35 @@ finish_prevtable_vardecl (prev, vars) import_export_vtable (vars, ctype, 1); return 1; } - + +/* We need to describe to the assembler the relationship between + a vtable and the vtable of the parent class. It is not + straightforward how to get this during multiple inheritance. */ + +static void +output_vtable_inherit (vars) + tree vars; +{ + tree parent; + rtx op[2]; + + op[0] = XEXP (DECL_RTL (vars), 0); /* strip the mem ref */ + + parent = binfo_for_vtable (vars); + + if (parent == TYPE_BINFO (DECL_CONTEXT (vars))) + op[1] = const0_rtx; + else if (parent) + { + parent = TYPE_BINFO_VTABLE (BINFO_TYPE (parent)); + op[1] = XEXP (DECL_RTL (parent), 0); /* strip the mem ref */ + } + else + my_friendly_abort (980826); + + output_asm_insn (".vtable_inherit %0, %1", op); +} + static int finish_vtable_vardecl (prev, vars) tree prev, vars; @@ -2716,6 +2748,10 @@ finish_vtable_vardecl (prev, vars) } rest_of_decl_compilation (vars, NULL_PTR, 1, 1); + + if (flag_vtable_gc) + output_vtable_inherit (vars); + return 1; } else if (! TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (vars))) diff --git a/gcc/cp/lang-options.h b/gcc/cp/lang-options.h index 25f642f1307..97ec99b38e7 100644 --- a/gcc/cp/lang-options.h +++ b/gcc/cp/lang-options.h @@ -93,6 +93,8 @@ DEFINE_LANG_NAME ("C++") { "-ftemplate-depth-", "Specify maximum template instantiation depth"}, { "-fthis-is-variable", "Make 'this' not be type '* const'" }, { "-fno-this-is-variable", "" }, + { "-fvtable-gc", "Discard unused virtual functions" }, + { "-fno-vtable-gc", "" }, { "-fvtable-thunks", "Implement vtables using thunks" }, { "-fno-vtable-thunks", "" }, { "-fweak", "Emit common-like symbols as weak symbols" }, diff --git a/gcc/cp/search.c b/gcc/cp/search.c index fe028c28e9c..fa3109daac6 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -2147,6 +2147,41 @@ dfs_walk (binfo, fn, qfn) fn (binfo); } +/* Like dfs_walk, but only walk until fn returns something, and return + that. We also use the real vbase binfos instead of the placeholders + in the normal binfo hierarchy. START is the most-derived type for this + hierarchy, so that we can find the vbase binfos. */ + +static tree +dfs_search (binfo, fn, start) + tree binfo, start; + tree (*fn) PROTO((tree)); +{ + tree binfos = BINFO_BASETYPES (binfo); + int i, n_baselinks = binfos ? TREE_VEC_LENGTH (binfos) : 0; + tree retval; + + for (i = 0; i < n_baselinks; i++) + { + tree base_binfo = TREE_VEC_ELT (binfos, i); + + if (TREE_CODE (BINFO_TYPE (base_binfo)) == TEMPLATE_TYPE_PARM + || TREE_CODE (BINFO_TYPE (base_binfo)) == TEMPLATE_TEMPLATE_PARM) + /* Pass */; + else + { + if (TREE_VIA_VIRTUAL (base_binfo) && start) + base_binfo = binfo_member (BINFO_TYPE (base_binfo), + CLASSTYPE_VBASECLASSES (start)); + retval = dfs_search (base_binfo, fn, start); + if (retval) + return retval; + } + } + + return fn (binfo); +} + static int markedp (binfo) tree binfo; { return BINFO_MARKED (binfo); } static int unmarkedp (binfo) tree binfo; @@ -3370,3 +3405,26 @@ types_overlap_p (empty_type, next_type) return found_overlap; } +/* Passed to dfs_search by binfo_for_vtable; determine if bvtable comes + from BINFO. */ + +static tree bvtable; +static tree +dfs_bfv_helper (binfo) + tree binfo; +{ + if (BINFO_VTABLE (binfo) == bvtable) + return binfo; + return NULL_TREE; +} + +/* Given a vtable VARS, determine which binfo it comes from. */ + +tree +binfo_for_vtable (vars) + tree vars; +{ + bvtable = vars; + return dfs_search (TYPE_BINFO (DECL_CONTEXT (vars)), dfs_bfv_helper, + DECL_CONTEXT (vars)); +}