From 4c256d17ae1675e17e5290a0ce5304c0df18ad41 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Fri, 24 Jan 2025 15:24:13 +0100 Subject: [PATCH] C++: Fix join-order problem in `UserType::getADeclarationEntry` Before on `LRMPUT/PUTSLAM`: ``` Evaluated recursive predicate UserType::UserType.getADeclarationEntry/0#cea44e2f@7ce3a4hu in 22069ms on iteration 2 (delta size: 379076853). Evaluated relational algebra for predicate UserType::UserType.getADeclarationEntry/0#cea44e2f@7ce3a4hu on iteration 2 running pipeline standard with tuple counts: 126825 ~32% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 379076853 ~97% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 379076853 ~97% {2} | JOIN WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev`(FIRST 2) 379076853 ~96% {2} | SCAN OUTPUT In.1, In.0 {2} | AND NOT `_Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs`(FIRST 2) 379076853 ~97% {2} | SCAN OUTPUT In.1, In.0 return r1 Evaluated named local _Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs@L0#7ce3a in 7459ms on iteration 2 (size: 0). Evaluated relational algebra for predicate _Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs@L0#7ce3a on iteration 2 running pipeline main with tuple counts: 126825 ~32% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 379076853 ~97% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 379076853 ~97% {2} | JOIN WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 379076853 ~97% {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev`(FIRST 2) 0 ~0% {2} r2 = r1 AND NOT usertypes_0#antijoin_rhs(FIRST 1) 0 ~0% {2} | JOIN WITH type_decls_1#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.0 379077929 ~95% {3} r3 = JOIN r1 WITH `ResolveClass::resolveClass/1#ea47deee_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 0 ~0% {2} | JOIN WITH type_decls_1#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.2 0 ~0% {2} r4 = r2 UNION r3 return r4 Evaluated named local _Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs@L0#7ce3a in 1031ms on iteration 3 (size: 0). Evaluated relational algebra for predicate _Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs@L0#7ce3a on iteration 3 running pipeline main with tuple counts: 190114100 ~0% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 0 ~0% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 0 ~0% {2} | JOIN WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 0 ~0% {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev`(FIRST 2) 0 ~0% {2} r2 = r1 AND NOT usertypes_0#antijoin_rhs(FIRST 1) 0 ~0% {2} | JOIN WITH type_decls_1#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.0 0 ~0% {3} r3 = JOIN r1 WITH `ResolveClass::resolveClass/1#ea47deee_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 0 ~0% {2} | JOIN WITH type_decls_1#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.2 0 ~0% {2} r4 = r2 UNION r3 return r4 Pipeline base for UserType::UserType.getADeclarationEntry/0#cea44e2f@7ce3a4hu was evaluated in 1 iterations totaling 149ms (delta sizes total: 224004). 149171 ~17% {1} r1 = JOIN type_decls_1#join_rhs WITH `ResolveClass::resolveClass/1#ea47deee` ON FIRST 1 OUTPUT Rhs.1 125649 ~0% {1} | JOIN WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0 1811817 ~1075% {2} | JOIN WITH `ResolveClass::resolveClass/1#ea47deee_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.0 1812281 ~1172% {2} | JOIN WITH type_decls_10#join_rhs ON FIRST 1 OUTPUT Lhs.1, Rhs.1 return r1 Pipeline standard for UserType::UserType.getADeclarationEntry/0#cea44e2f@7ce3a4hu was evaluated in 1 iterations totaling 22069ms (delta sizes total: 379076853). 126825 ~32% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 379076853 ~97% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 379076853 ~97% {2} | JOIN WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev`(FIRST 2) 379076853 ~96% {2} | SCAN OUTPUT In.1, In.0 {2} | AND NOT `_Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs`(FIRST 2) 379076853 ~97% {2} | SCAN OUTPUT In.1, In.0 return r1 Pipeline main for _Class::Class#9afdbffd_Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs_ResolveClass::r__#antijoin_rhs@L0#7ce3a was evaluated in 2 iterations totaling 8490ms (sizes total: 0). 190240925 ~0% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 379076853 ~97% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 379076853 ~97% {2} | JOIN WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 379076853 ~97% {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#prev`(FIRST 2) 0 ~0% {2} r2 = r1 AND NOT usertypes_0#antijoin_rhs(FIRST 1) 0 ~0% {2} | JOIN WITH type_decls_1#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.0 379077929 ~95% {3} r3 = JOIN r1 WITH `ResolveClass::resolveClass/1#ea47deee_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1, Lhs.0 0 ~0% {2} | JOIN WITH type_decls_1#join_rhs ON FIRST 1 OUTPUT Lhs.1, Lhs.2 0 ~0% {2} r4 = r2 UNION r3 return r4 ``` After: ``` [2025-01-24 15:22:19] Evaluated non-recursive predicate UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a@82645fgi in 8ms (size: 149661). Evaluated relational algebra for predicate UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a@82645fgi with tuple counts: 149661 ~5% {2} r1 = JOIN `_ResolveClass::resolveClass/1#ea47deee_type_decls#shared` WITH UserType::UserType#21e9e463 ON FIRST 1 OUTPUT Lhs.0, Lhs.1 return r1 [2025-01-24 15:22:19] Evaluated non-recursive predicate project#UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a@22da38r1 in 10ms (size: 124540). Evaluated relational algebra for predicate project#UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a@22da38r1 with tuple counts: 149661 ~17% {1} r1 = SCAN `UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a` OUTPUT In.0 124540 ~0% {1} | STREAM DEDUP return r1 [2025-01-24 15:22:20] Evaluated non-recursive predicate Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs@9c5150lt in 91ms (size: 764244). Evaluated relational algebra for predicate Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs@9c5150lt with tuple counts: 764244 ~2% {2} r1 = SCAN `Class::Class.isConstructedFrom/1#dispred#390e01c8` OUTPUT In.1, In.0 return r1 Evaluated recursive predicate UserType::UserType.getADeclarationEntry/0#cea44e2f#fb@d5a154u4 in 8ms on iteration 1 (delta size: 25561). Evaluated relational algebra for predicate UserType::UserType.getADeclarationEntry/0#cea44e2f#fb@d5a154u4 on iteration 1 running pipeline base with tuple counts: 149661 ~2% {2} r1 = SCAN `UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a` OUTPUT In.1, In.0 25561 ~6% {2} | JOIN WITH type_def ON FIRST 1 OUTPUT Lhs.1, Lhs.0 return r1 Evaluated recursive predicate UserType::UserType.getADeclarationEntry/0#cea44e2f#fb@d5a154u4 in 108ms on iteration 2 (delta size: 763236). Evaluated relational algebra for predicate UserType::UserType.getADeclarationEntry/0#cea44e2f#fb@d5a154u4 on iteration 2 running pipeline standard with tuple counts: 3655 ~0% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#fb#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 763236 ~1% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 763236 ~1% {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#fb#prev`(FIRST 2) 763236 ~1% {2} | AND NOT `project#UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a`(FIRST 1) return r1 Pipeline base for UserType::UserType.getADeclarationEntry/0#cea44e2f#fb@d5a154u4 was evaluated in 1 iterations totaling 8ms (delta sizes total: 25561). 149661 ~2% {2} r1 = SCAN `UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a` OUTPUT In.1, In.0 25561 ~6% {2} | JOIN WITH type_def ON FIRST 1 OUTPUT Lhs.1, Lhs.0 return r1 Pipeline standard for UserType::UserType.getADeclarationEntry/0#cea44e2f#fb@d5a154u4 was evaluated in 1 iterations totaling 108ms (delta sizes total: 763236). 3655 ~0% {2} r1 = JOIN `UserType::UserType.getADeclarationEntry/0#cea44e2f#fb#prev_delta` WITH Class::Class#9afdbffd ON FIRST 1 OUTPUT Lhs.0, Lhs.1 763236 ~1% {2} | JOIN WITH `Class::Class.isConstructedFrom/1#dispred#390e01c8_10#join_rhs` ON FIRST 1 OUTPUT Rhs.1, Lhs.1 763236 ~1% {2} | AND NOT `UserType::UserType.getADeclarationEntry/0#cea44e2f#fb#prev`(FIRST 2) 763236 ~1% {2} | AND NOT `project#UserType::UserType.getADeclarationEntryBase/0#dispred#d07c0e9a`(FIRST 1) return r1 ``` --- cpp/ql/lib/semmle/code/cpp/UserType.qll | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/UserType.qll b/cpp/ql/lib/semmle/code/cpp/UserType.qll index 439ed84e05e8..ce2495b4d252 100644 --- a/cpp/ql/lib/semmle/code/cpp/UserType.qll +++ b/cpp/ql/lib/semmle/code/cpp/UserType.qll @@ -47,10 +47,15 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @ else result = this.getADeclarationLocation() } + private TypeDeclarationEntry getADeclarationEntryBase() { + type_decls(underlyingElement(result), unresolveElement(this), _) + } + override TypeDeclarationEntry getADeclarationEntry() { - if type_decls(_, unresolveElement(this), _) - then type_decls(underlyingElement(result), unresolveElement(this), _) - else exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry()) + pragma[only_bind_into](result) = pragma[only_bind_into](this).getADeclarationEntryBase() + or + not exists(this.getADeclarationEntryBase()) and + exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry()) } override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }