From patchwork Mon Jul 6 17:03:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas de Bock X-Patchwork-Id: 138606 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BF49A4BA2E2D for ; Mon, 6 Jul 2026 17:04:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org BF49A4BA2E2D Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=ScMWUdjJ X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lf1-x129.google.com (mail-lf1-x129.google.com [IPv6:2a00:1450:4864:20::129]) by sourceware.org (Postfix) with ESMTPS id D7B1B4BA2E13 for ; Mon, 6 Jul 2026 17:04:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D7B1B4BA2E13 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D7B1B4BA2E13 Authentication-Results: sourceware.org; arc=pass smtp.remote-ip=2a00:1450:4864:20::129 ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1783357447; cv=pass; b=Vhg2NlwR/uwokZdpx6AeTMkEKZGHnZoMrN8Mqq+5IrmWw3cVRffy5DIM7XjuZ6G12PjnDIXHkgRKndPKMpjvG5aYszd7SAPzqAeAqMhGYlhSjJBjSF36Ol1dbHcRc3eZ0g5RRc03GMCzJhQRseeCn1xZSCTN9xNJNMei8fyFiu0= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1783357447; c=relaxed/simple; bh=I4vbe6+K0s5nj97mQ1Szos/bdINvQ08WIA/PWNJIDYs=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=xDHx36zu0tzW8Pk/ek+VNK8QhqrjwYJIqnzbe3dUoyinEGLr9qYbqcQ1pq9txaDmcv8KZGXn8V+puzh9dH4GzJH35CLushrVVa0xfe8Lql1MnqaarHXlAax/vEw/wtdif+ah0s2hVvLaIkehOq+Ji0jIkCEcfiFTvRygZnlwl9g= ARC-Authentication-Results: i=2; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=ScMWUdjJ DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D7B1B4BA2E13 Received: by mail-lf1-x129.google.com with SMTP id 2adb3069b0e04-5aeb89359a3so2610551e87.3 for ; Mon, 06 Jul 2026 10:04:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783357445; cv=none; d=google.com; s=arc-20260327; b=bIS0eABEplBX7B804ltnwOG4q9BQSJOKQBhZnhc/G6SCTUCPo/cYeA60F+G1TsYaLo tlndx2OVd/85GxthVbtjfdOZ/LpWOyiCdiXBdrj7ZeEGp2WBkXm0FPTDf8YeRsyckTos YauN8eJOSb8riMzA/lnozU5ZA6rp267aCKGEUtVdfCSSViHe/Z71AZyFIFW72PpLFkNF paSQ59Hoteq2RFTEAK8685/N/f4vSSqO9cjpEB2HoJDmLhkN+9In+vVJNb+y0OwG3cI1 3n0WD3CZQgoVKyf/S3Q+us+sVUeWRYL6rsxaexKdCPR5zziqG1P0pSq5FO4/8rzIu32Y P/0g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:mime-version:dkim-signature; bh=+mYgmyaU5qUufgnRJPBi9Im5eS1Dzpn8tcT6Xe7MZK4=; fh=QQB+HOdnkgaFqzadz/wXY0nagyvNwqchu8Yqt7oTyHE=; b=UVNivOLHFYIo2hrvv6KEpKoKhBHomvRbzlhAfhqymb62oIaK6Shsjkcnv2HZ5pdQMe W/SIsBjuGwXlZzBPhuBku/r5Sio5lC8CEh8HhqxwO1A05mIw5jYxjmjJEC682g6mANha KXREXNhyjHWTimlhiQjyVdauoX1m0OkY4eULRCB+4Oh/ea4eONahGanf4iQRw3UNjab+ 413LGn2Mu08pg+XHU3k2KDDItTHXwzZOc3lfoauNasKXRfvWjZ68BPBFH1bza8B3j/DK wiISv8b/nKkzRpz/xEs0jpuvVMG5Ge3jlk3urktPw33n6oEf8jMoxv2jvhbk8p5VKSvV 4ZDA==; darn=gcc.gnu.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783357445; x=1783962245; darn=gcc.gnu.org; h=content-type:cc:to:subject:message-id:date:from:mime-version:from :to:cc:subject:date:message-id:reply-to:content-type; bh=+mYgmyaU5qUufgnRJPBi9Im5eS1Dzpn8tcT6Xe7MZK4=; b=ScMWUdjJZE5dauXsCyjV+Kjnb7YISvbKrNlGRNw6bE7gGNBHWKnqtUOsGTvsKg8LuA ZI2ZjvIl7FIGUcFt1kaIGB/kBMDbmoMRfUkyXYm1YEuPKQueC3xQ6K0ds+oIxQcZ0NBt sDjQf2l8Xv5X45pAtxYp7FIx4M8fAUhE212Da8sxWGn/4v32znYc3KJod+Bk7eKoeAQi GbDxmxVDn89VEkATMBI+gHZcaUf5fmv2RplZjXRDyQmRSoe3vmh0/hxKxFfyYKVgb0U3 KlCgVNdRRRDrWF9B3AnGv70uz+9MQRcO08RvUc6dGWZXkCeYl5d42pD/5kV3w0HCtSTH R3QA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783357445; x=1783962245; h=content-type:cc:to:subject:message-id:date:from:mime-version :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=+mYgmyaU5qUufgnRJPBi9Im5eS1Dzpn8tcT6Xe7MZK4=; b=nPckXQHX9EsCxfS6neY+4HIY9iFhS2yOsp7/cQSaiamP1p7Xu4x+KKMMpL8uI8UtKC A7KFdtG3jTrQB/k9i+jiiS6kr8Czgi7j7wuykz3V0nqobqJFv1302ZcPscAX8OFjSMqa 6j5be1uWaUPVVbD1egbg3t5POL8F5gVgXGu/G7lKXBXRnb2iaWpl62vXO8rHLJ0kYi6E lRLF3gx9kDjrlOTf2ARsoiMqYPjvL1GPfMVBRl0zXE2kej74fUbEU6bwSPQwI2MVdj8f qiig91wx7Y8YvwxBbC4fYcF9ch7XbXMdSWeckxEdipaHgLw+oJXKT3pN4WZBhLb2WKGt 7eWw== X-Gm-Message-State: AOJu0Yyt9RHwTeXzuPBiw1P3LkRG+jVdF+J8zBlgNj6r2c9ZlFZD+wzl 4dIsCxFACnN6Fe3XRqraQWZ+NsEjWYH49aZojJ5ConV4UEfJla9VNkpk0IiDFA6YofyF0eMYzNp iBq6xC0AyfCZk3xy+BpeqM7KQ4e4V1Pf/tRN4ZyQ= X-Gm-Gg: AfdE7clgyPnqsG4Jz7/LClTyuNh6Q/LOahcJnZ9eUt1gWB8+RvF16TQGrOYPDHdsis8 iJ8AuJEEpr0jV+QMWoc0sAiA7WYs7e6+f6AHx9eh5ogmXj9fiTstS5wCygh22tVI1Rz14LmEFWh 7QWxoxY7dbhKl4laJJFHk0FQDCnvE+Jfbc44Vm9IrSRSPxMN38/esXSkveQtcIkd+NVCrwROq01 qIb3XIw1Eg3co1uG/9NdAFD0A28llJIrgNnMgMJ2Wlm4u94U3Cg3GrV/eF8oeougUvaM2s+sGmY OCDaegmMY9t4wfZiVKgjVrpYNiM= X-Received: by 2002:a05:6512:8347:b0:5ae:d1f6:32c8 with SMTP id 2adb3069b0e04-5b007c4a0aamr259690e87.46.1783357445085; Mon, 06 Jul 2026 10:04:05 -0700 (PDT) MIME-Version: 1.0 From: Thomas de Bock Date: Mon, 6 Jul 2026 19:03:54 +0200 X-Gm-Features: AVVi8CdWRqKTNBKARCmFchT64VQHayh4zQemXvJhJIBRAa-7g8CI_j5ZeXN8pnc Message-ID: Subject: [PATCH v2] c++: Optimize dynamic_cast to final class [PR63164] To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, "jakub@redhat.com" X-Spam-Status: No, score=-8.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org This is a revised version of my earlier dynamic_cast final type patch: https://gcc.gnu.org/pipermail/gcc-patches/2026-January/706155.html The previous version added -fassume-unique-vtables and used a vptr comparison for final targets when that option was enabled. This version drops the option and only optimizes the cases discussed as safe in PR c++/63164: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63164 For dynamic_cast to a final target type, when the source static type is an accessible unambiguous base of the final target and the target vtable has external non-vague linkage, the ABI gives the vtable a single emitted definition. In that case the frontend can compare the source object's vptr against the target vtable address to test whether the dynamic type is exactly the final target type. The patch also handles final targets with no base of the source static type. Such casts can never succeed, so pointer casts are folded to null and reference casts are folded to std::bad_cast, while preserving evaluation of the dynamic_cast operand. gcc/cp/ChangeLog: PR c++/63164 * rtti.cc (build_dynamic_cast_1): Fold impossible dynamic_casts to final target types. Use a vptr comparison when the final target vtable has external non-vague linkage. gcc/testsuite/ChangeLog: PR c++/63164 * g++.dg/rtti/dyncast9.C: New test. * g++.dg/rtti/dyncast10.C: New test. * g++.dg/rtti/dyncast11.C: New test. Signed-off-by: Thomas de Bock --- Changes since v1: - Drop the -fassume-unique-vtables option. - Restrict vptr comparison to final targets whose vtable has external non-vague linkage. - Fold impossible casts when the final target has no base of the source static type. - Add tests for the optimized cases and for fallback cases where __dynamic_cast is still needed. gcc/cp/rtti.cc | 107 +++++++++++++++++++++++++++++++--- gcc/testsuite/g++.dg/rtti/dyncast10.C | 39 +++++++++++++ gcc/testsuite/g++.dg/rtti/dyncast11.C | 43 ++++++++++++++ gcc/testsuite/g++.dg/rtti/dyncast9.C | 53 +++++++++++++++++ 4 files changed, 235 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/g++.dg/rtti/dyncast10.C create mode 100644 gcc/testsuite/g++.dg/rtti/dyncast11.C create mode 100644 gcc/testsuite/g++.dg/rtti/dyncast9.C diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc index 1e787157b7b..802c012f9b1 100644 --- a/gcc/cp/rtti.cc +++ b/gcc/cp/rtti.cc @@ -745,6 +745,106 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr, target_type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); static_type = TYPE_MAIN_VARIANT (TREE_TYPE (exprtype)); + + /* Since expr is used twice below, save it. */ + expr = save_expr (expr); + + expr1 = expr; + if (tc == REFERENCE_TYPE) + expr1 = cp_build_addr_expr (expr1, complain); + + /* A final class, or a class with a final virtual destructor, cannot + be further derived. */ + bool final_p = TYPE_FINAL_P (target_type); + for (tree v = BINFO_VIRTUALS (TYPE_BINFO (target_type)); + v && !final_p; v = TREE_CHAIN (v)) + if (DECL_DESTRUCTOR_P (BV_FN (v)) && DECL_FINAL_P (BV_FN (v))) + final_p = true; + + if (final_p) + { + /* If source static type is not a base of target type, the cast + can never succeed. */ + tree binfo = lookup_base (target_type, static_type, ba_any, + NULL, tf_none); + if (!binfo) + { + if (complain & tf_warning) + warning_at (loc, 0, + "%(%E)%> can never succeed", + type, old_expr); + if (tc == REFERENCE_TYPE) + { + tree bad = throw_bad_cast (); + TREE_TYPE (bad) = type; + return cp_build_compound_expr (expr, bad, complain); + } + return build_if_nonnull (expr, build_zero_cst (type), + complain); + } + + binfo = lookup_base (target_type, static_type, ba_check, + NULL, tf_none); + if (binfo && binfo != error_mark_node) + { + tree vtbl_binfo = BINFO_VTABLE (binfo) ? binfo + : TYPE_BINFO (target_type); + tree vtbl = get_vtbl_decl_for_binfo (vtbl_binfo); + bool unique_vtable = false; + if (!CLASSTYPE_USE_TEMPLATE (target_type)) + { + tree cdecl = TYPE_NAME (target_type); + if (DECL_LANG_SPECIFIC (cdecl) + && DECL_MODULE_ATTACH_P (cdecl)) + unique_vtable = true; + else + { + tree key_method = CLASSTYPE_KEY_METHOD (target_type); + if (key_method + && !DECL_DECLARED_INLINE_P (key_method)) + unique_vtable = true; + } + } + + /* For a final target, the ba_check lookup found an + accessible unambiguous STATIC_TYPE base. With an external + non-vague target vtable, vptr equality tests whether the + dynamic type is exactly TARGET_TYPE. */ + if (vtbl && TREE_PUBLIC (vtbl) && unique_vtable) + { + tree succ = build_base_path (MINUS_EXPR, expr1, binfo, + true, tf_none); + if (succ != error_mark_node) + { + tree trgt_vptr = build_vtbl_address (vtbl_binfo); + tree src_obj = cp_build_fold_indirect_ref (expr1); + tree src_vptr = build_vfield_ref (src_obj, + static_type); + tree cond = cp_build_binary_op (loc, EQ_EXPR, + trgt_vptr, src_vptr, + complain); + if (cond == error_mark_node) + return error_mark_node; + if (tc != REFERENCE_TYPE) + succ = cp_convert (type, succ, complain); + if (succ == error_mark_node) + return error_mark_node; + + tree fail = (tc == REFERENCE_TYPE + ? throw_bad_cast () + : build_zero_cst (type)); + tree result = build3 (COND_EXPR, TREE_TYPE (succ), + cond, succ, fail); + SET_EXPR_LOCATION (result, loc); + + if (tc == REFERENCE_TYPE) + return cp_convert (type, result, complain); + return build_if_nonnull (expr, result, complain); + } + } + } + } + td2 = get_tinfo_decl (target_type); if (!mark_used (td2, complain) && !(complain & tf_error)) return error_mark_node; @@ -757,13 +857,6 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr, /* Determine how T and V are related. */ boff = dcast_base_hint (static_type, target_type); - /* Since expr is used twice below, save it. */ - expr = save_expr (expr); - - expr1 = expr; - if (tc == REFERENCE_TYPE) - expr1 = cp_build_addr_expr (expr1, 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..d96b16b8e91 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/dyncast10.C @@ -0,0 +1,39 @@ +// PR c++/63164 +// { dg-do run { target c++11 } } +// { dg-additional-options "-fdump-tree-original" } +// { dg-final { scan-tree-dump-not "__dynamic_cast" "original" } } + +extern "C" void abort (); + +int calls; + +struct A { virtual ~A (); }; +A::~A () {} + +struct D final { virtual ~D (); }; +D::~D () {} + +A *get (A *a) { ++calls; return a; } +A& getr (A& a) { ++calls; return a; } + +bool f (A *a) +{ + return dynamic_cast (get (a)); // { dg-warning "can never succeed" } +} + +bool g (A& a) +{ + try + { + dynamic_cast (getr (a)); // { dg-warning "can never succeed" } + return true; + } + catch (...) { return false; } +} + +int main () +{ + A a; + if (f (&a) || g (a) || calls != 2) + abort (); +} diff --git a/gcc/testsuite/g++.dg/rtti/dyncast11.C b/gcc/testsuite/g++.dg/rtti/dyncast11.C new file mode 100644 index 00000000000..9a62a815d85 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/dyncast11.C @@ -0,0 +1,43 @@ +// PR c++/63164 +// { dg-do compile { target c++11 } } +// { dg-additional-options "-fdump-tree-original" } +// { dg-final { scan-tree-dump-times "__dynamic_cast" 6 "original" } } + +struct A { virtual ~A () {} }; + +/* No key function, so the vtable has vague linkage. */ +struct Vague final : A { }; +Vague *f1 (A *a) { return dynamic_cast (a); } + +/* A vptr comparison against a single A subobject is not enough when the + target has multiple A bases. */ +struct X : A { }; +struct Y : A { }; +struct Amb final : X, Y { virtual void key (); }; +void Amb::key () {} +Amb *f2 (A *a) { return dynamic_cast (a); } + +/* The source type must be a public base of the target. */ +struct Priv final : private A { virtual void key (); }; +void Priv::key () {} +Priv *f3 (A *a) { return dynamic_cast (a); } + +/* Nor can we use a fixed offset from a virtual base. */ +struct VBase final : virtual A { virtual void key (); }; +void VBase::key () {} +VBase *f4 (A *a) { return dynamic_cast (a); } + +/* Template class vtables are not uniquely emitted. */ +template +struct Tmpl final : A { virtual void key (); }; +template +void Tmpl::key () {} +Tmpl *f5 (A *a) { return dynamic_cast *> (a); } + +/* The target vtable must have external linkage. */ +namespace +{ +struct Local final : A { virtual void key (); }; +void Local::key () {} +} +Local *f6 (A *a) { return dynamic_cast (a); } diff --git a/gcc/testsuite/g++.dg/rtti/dyncast9.C b/gcc/testsuite/g++.dg/rtti/dyncast9.C new file mode 100644 index 00000000000..d593048df38 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/dyncast9.C @@ -0,0 +1,53 @@ +// PR c++/63164 +// { dg-do run { target c++11 } } +// { dg-additional-options "-fdump-tree-original" } +// { dg-final { scan-tree-dump-not "__dynamic_cast" "original" } } + +extern "C" void abort (); + +struct A { virtual ~A (); }; +A::~A () {} + +struct B : A +{ + ~B () final; + void p () { ++i; } + int i = 0; +}; +B::~B () {} + +struct C final : A { ~C (); }; +C::~C () {} + +struct X { virtual ~X (); int x; }; +X::~X () {} + +struct D final : X, A { virtual void key (); }; +void D::key () {} + +B *f (A *a) { return dynamic_cast (a); } +C *h (A *a) { return dynamic_cast (a); } +D *k (A *a) { return dynamic_cast (a); } + +bool g (A& a) +{ + try { dynamic_cast (a).p (); return true; } + catch (...) { return false; } +} + +int main () +{ + A a; + B b; + C c; + D d; + + if (f (&a) || h (&a) || k (&a) || g (a)) + abort (); + if (f (&b) != &b || h (&b) || k (&b) || !g (b) || b.i != 1) + abort (); + if (f (&c) || h (&c) != &c || k (&c) || g (c)) + abort (); + if (f (&d) || h (&d) || k (&d) != &d || g (d)) + abort (); +}