Notes: This patch optimizes out calls to __dynamic_cast when the type being cast to is final (or its destructor), replacing them with a simple comparison of the types' vtable addresses. This is already implemented and done by default in clang (https://review s.llvm.org/D154658), but can be turned off with -fno-assume-unique-vtables, due to the problems it can cause with unmerged vtables when using shared libraries. With the optimization not really fitting well under any existing flag (that I know of), I believe an -fassume-unique-vtables flag (or something similar) would be appropriate to add to gcc, which besides enabling this optimization, should probably also define __GXX_MERGED_TYPEINFO_NAMES as 1. I believe instead of adding this flag and the vtable addr comparison logic, just replacing __dynamic_cast with type_info::operator== (whose behaviour depends on __GXX_MERGED_TYPEINFO_NAMES) is insufficient as it performs additional dereferences compared to a simple vtable addr comparison, still leaving room for further optimization.
gcc/c-family/c.opt | 4 ++ gcc/cp/rtti.cc | 55 +++++++++++++++++++++++++ gcc/testsuite/g++.dg/rtti/dyncast10.C | 58 +++++++++++++++++++++++++++ gcc/testsuite/g++.dg/rtti/dyncast9.C | 58 +++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 gcc/testsuite/g++.dg/rtti/dyncast10.C create mode 100644 gcc/testsuite/g++.dg/rtti/dyncast9.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 12877eb0e17..b597df70bfe 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1719,6 +1719,10 @@ fassume-sane-operators-new-delete C++ ObjC++ Optimization Var(flag_assume_sane_operators_new_delete) Init(1) Assume C++ replaceable global operators new, new[], delete, delete[] don't read or write visible global state. +fassume-unique-vtables +C++ ObjC++ Optimization Var(flag_assume_unique_vtables) Init(0) +Assume that, throughout the program's lifetime, each pair of class types representing the same class shares a common vtable. + ; Define extra predefined macros for use in libgcc. fbuilding-libgcc C ObjC C++ ObjC++ Undocumented Var(flag_building_libgcc) diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc index c06a18b3ff1..7227386ddbf 100644 --- a/gcc/cp/rtti.cc +++ b/gcc/cp/rtti.cc @@ -761,6 +761,61 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr, if (tc == REFERENCE_TYPE) expr1 = cp_build_addr_expr (expr1, complain); + + + /* If type is final, don't call dynamic_cast. + * Instead just check vtable equivalence at runtime. + * TYPE_FINAL_P does not return true for non-final class with + * final destructor overriding virtual though, + * so look through virtual functions for final destructor */ + + bool can_inherit = true; + if(flag_assume_unique_vtables) + { + can_inherit = !TYPE_FINAL_P (target_type); + tree vchain; + for (vchain = BINFO_VIRTUALS (TYPE_BINFO (target_type)); + vchain && can_inherit; + vchain = TREE_CHAIN (vchain)) + { + if (!DECL_DESTRUCTOR_P (BV_FN (vchain))) + continue; + if (!DECL_FINAL_P (BV_FN (vchain))) + continue; + can_inherit = false; + } + } + + if (!can_inherit) + { + tree binfo = lookup_base (target_type, static_type, ba_check, NULL, complain); + + if (!binfo || binfo == error_mark_node) + return error_mark_node; + + /* Retrieve vtable declaration and address. + * The offset-to-top field is adjusted for here. */ + tree trgt_vptr = build_vtbl_address ( + BINFO_VTABLE (binfo) ? binfo : TYPE_BINFO (target_type)); + + tree src_obj = cp_build_fold_indirect_ref (expr); + tree src_vptr = build_vfield_ref (src_obj, static_type); + + tree fail_result = tc == REFERENCE_TYPE ? throw_bad_cast () : nullptr_node; + tree succ_result = build_base_path (MINUS_EXPR, expr1, binfo, true, complain); + + /* Check vtable equivalence by vptr address */ + tree cond = build2 (NE_EXPR, boolean_type_node, trgt_vptr, src_vptr); + tree result = build3 (COND_EXPR, type, cond, fail_result, succ_result); + + SET_EXPR_LOCATION (result, loc); + + if (tc == REFERENCE_TYPE) + return result; + + return build_if_nonnull (expr, result, complain); + } + elems[0] = expr1; elems[1] = td3; elems[2] = td2; diff --git a/gcc/testsuite/g++.dg/rtti/dyncast10.C b/gcc/testsuite/g++.dg/rtti/dyncast10.C new file mode 100644 index 00000000000..e4c9b303cef --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/dyncast10.C @@ -0,0 +1,58 @@ +// { dg-options "-fassume-unique-vtables" } +// { dg-final { scan-tree-dump-not "__dynamic_cast" "original" } } + +extern "C" void abort (); + +struct A +{ + virtual ~A () {} +}; + +struct B final : public A +{ + ~B() {} + void p () { c++; } + int c; +}; +struct C final : public A +{ + ~C() {} + void f () { c++; } + int c; +}; + + +bool f (A* a) { + return dynamic_cast<B*>(a) != nullptr; +} +bool g (A& a) { + try + { + dynamic_cast<B&>(a).p(); + return true; + } + catch (...) + { + return false; + } +} + +int +main (void) +{ + A a; + B b; + C c; + if (f (&a)) + abort (); + if (g (a)) + abort (); + if (!f (&b)) + abort (); + if (!g (b)) + abort (); + if (f (&c)) + abort (); + if (g (c)) + abort (); +} diff --git a/gcc/testsuite/g++.dg/rtti/dyncast9.C b/gcc/testsuite/g++.dg/rtti/dyncast9.C new file mode 100644 index 00000000000..4f3b2348d13 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/dyncast9.C @@ -0,0 +1,58 @@ +// { dg-options "-fassume-unique-vtables" } +// { dg-final { scan-tree-dump-not "__dynamic_cast" "original" } } + +extern "C" void abort (); + +struct A +{ + virtual ~A () {} +}; + +struct B : public A +{ + ~B() final {} + void p () { c++; } + int c; +}; +struct C : public A +{ + ~C() final {} + void f () { c++; } + int c; +}; + + +bool f (A* a) { + return dynamic_cast<B*>(a) != nullptr; +} +bool g (A& a) { + try + { + dynamic_cast<B&>(a).p(); + return true; + } + catch (...) + { + return false; + } +} + +int +main (void) +{ + A a; + B b; + C c; + if (f (&a)) + abort (); + if (g (a)) + abort (); + if (!f (&b)) + abort (); + if (!g (b)) + abort (); + if (f (&c)) + abort (); + if (g (c)) + abort (); +} -- 2.34.1 This e-mail and any attachments may contain information that is confidential and proprietary and otherwise protected from disclosure. If you are not the intended recipient of this e-mail, do not read, duplicate or redistribute it by any means. Please immediately delete it and any attachments and notify the sender that you have received it by mistake. Unintended recipients are prohibited from taking action on the basis of information in this e-mail or any attachments. The DRW Companies make no representations that this e-mail or any attachments are free of computer viruses or other defects.