From: Timur Iskhodzhanov Date: Sun, 6 Oct 2013 15:31:37 +0000 (+0000) Subject: Fix PR17382 - properly group virtual method overloads in the vftable X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=cbf8dde9e39ab8e95477e3bdf4ee0d269fac0637;p=clang Fix PR17382 - properly group virtual method overloads in the vftable git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@192067 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index c28aeefd99..9aad711748 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -2730,6 +2730,38 @@ FindDirectlyOverriddenMethodInBases(const CXXMethodDecl *MD, return 0; } +static void GroupNewVirtualOverloads( + const CXXRecordDecl *RD, + SmallVector &VirtualMethods) { + // Put the virtual methods into VirtualMethods in the proper order: + // 1) Group overloads by declaration name. New groups are added to the + // vftable in the order of their first declarations in this class + // (including overrides). + // 2) In each group, new overloads appear in the reverse order of declaration. + typedef SmallVector MethodGroup; + SmallVector Groups; + typedef llvm::DenseMap VisitedGroupIndicesTy; + VisitedGroupIndicesTy VisitedGroupIndices; + for (CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + if (!MD->isVirtual()) + continue; + + VisitedGroupIndicesTy::iterator J; + bool Inserted; + llvm::tie(J, Inserted) = VisitedGroupIndices.insert( + std::make_pair(MD->getDeclName(), Groups.size())); + if (Inserted) + Groups.push_back(MethodGroup(1, MD)); + else + Groups[J->second].push_back(MD); + } + + for (unsigned I = 0, E = Groups.size(); I != E; ++I) + VirtualMethods.append(Groups[I].rbegin(), Groups[I].rend()); +} + void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, const CXXRecordDecl *LastVBase, BasesSetVectorTy &VisitedBases) { @@ -2766,6 +2798,10 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, llvm_unreachable("Found a duplicate primary base!"); } + SmallVector VirtualMethods; + // Put virtual methods in the proper order. + GroupNewVirtualOverloads(RD, VirtualMethods); + // Now go through all virtual member functions and add them to the current // vftable. This is done by // - replacing overridden methods in their existing slots, as long as they @@ -2774,12 +2810,8 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, // sub-bases; // - adding new slots for methods that require Return adjustment. // We keep track of the methods visited in the sub-bases in MethodInfoMap. - for (CXXRecordDecl::method_iterator I = RD->method_begin(), - E = RD->method_end(); I != E; ++I) { - const CXXMethodDecl *MD = *I; - - if (!MD->isVirtual()) - continue; + for (unsigned I = 0, E = VirtualMethods.size(); I != E; ++I) { + const CXXMethodDecl *MD = VirtualMethods[I]; FinalOverriders::OverriderInfo Overrider = Overriders.getOverrider(MD, Base.getBaseOffset()); diff --git a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp index 82e8f1c516..4a0a76dbf2 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp @@ -9,6 +9,11 @@ // RUN: FileCheck --check-prefix=CHECK-F %s < %t // RUN: FileCheck --check-prefix=CHECK-G %s < %t // RUN: FileCheck --check-prefix=CHECK-I %s < %t +// RUN: FileCheck --check-prefix=CHECK-J %s < %t +// RUN: FileCheck --check-prefix=CHECK-K %s < %t +// RUN: FileCheck --check-prefix=CHECK-L %s < %t +// RUN: FileCheck --check-prefix=CHECK-M %s < %t +// RUN: FileCheck --check-prefix=CHECK-N %s < %t struct A { // CHECK-A: VFTable for 'A' (3 entries) @@ -149,3 +154,99 @@ struct I : Empty { }; I i; + +struct J { + // CHECK-J: VFTable for 'J' (6 entries) + // CHECK-J-NEXT: 0 | void J::foo(long) + // CHECK-J-NEXT: 1 | void J::foo(int) + // CHECK-J-NEXT: 2 | void J::foo(short) + // CHECK-J-NEXT: 3 | void J::bar(long) + // CHECK-J-NEXT: 4 | void J::bar(int) + // CHECK-J-NEXT: 5 | void J::bar(short) + virtual void foo(short); + virtual void bar(short); + virtual void foo(int); + virtual void bar(int); + virtual void foo(long); + virtual void bar(long); +}; + +J j; + +struct K : J { + // CHECK-K: VFTable for 'J' in 'K' (9 entries) + // CHECK-K-NEXT: 0 | void J::foo(long) + // CHECK-K-NEXT: 1 | void J::foo(int) + // CHECK-K-NEXT: 2 | void J::foo(short) + // CHECK-K-NEXT: 3 | void J::bar(long) + // CHECK-K-NEXT: 4 | void J::bar(int) + // CHECK-K-NEXT: 5 | void J::bar(short) + // CHECK-K-NEXT: 6 | void K::bar(double) + // CHECK-K-NEXT: 7 | void K::bar(float) + // CHECK-K-NEXT: 8 | void K::foo(float) + virtual void bar(float); + virtual void foo(float); + virtual void bar(double); +}; + +K k; + +struct L : J { + // CHECK-L: VFTable for 'J' in 'L' (9 entries) + // CHECK-L-NEXT: 0 | void J::foo(long) + // CHECK-L-NEXT: 1 | void L::foo(int) + // CHECK-L-NEXT: 2 | void J::foo(short) + // CHECK-L-NEXT: 3 | void J::bar(long) + // CHECK-L-NEXT: 4 | void J::bar(int) + // CHECK-L-NEXT: 5 | void J::bar(short) + // CHECK-L-NEXT: 6 | void L::foo(float) + // CHECK-L-NEXT: 7 | void L::bar(double) + // CHECK-L-NEXT: 8 | void L::bar(float) + + // This case is interesting. Since the J::foo(int) override is the first method in + // the class, foo(float) precedes the bar(double) and bar(float) in the vftable. + virtual void foo(int); + virtual void bar(float); + virtual void foo(float); + virtual void bar(double); +}; + +L l; + +struct M : J { + // CHECK-M: VFTable for 'J' in 'M' (11 entries) + // CHECK-M-NEXT: 0 | void J::foo(long) + // CHECK-M-NEXT: 1 | void M::foo(int) + // CHECK-M-NEXT: 2 | void J::foo(short) + // CHECK-M-NEXT: 3 | void J::bar(long) + // CHECK-M-NEXT: 4 | void J::bar(int) + // CHECK-M-NEXT: 5 | void J::bar(short) + // CHECK-M-NEXT: 6 | void M::foo(float) + // CHECK-M-NEXT: 7 | void M::spam(long) + // CHECK-M-NEXT: 8 | void M::spam(int) + // CHECK-M-NEXT: 9 | void M::bar(double) + // CHECK-M-NEXT: 10 | void M::bar(float) + + virtual void foo(int); + virtual void spam(int); + virtual void bar(float); + virtual void bar(double); + virtual void foo(float); + virtual void spam(long); +}; + +M m; + +struct N { + // CHECK-N: VFTable for 'N' (4 entries) + // CHECK-N-NEXT: 0 | void N::operator+(int) + // CHECK-N-NEXT: 1 | void N::operator+(short) + // CHECK-N-NEXT: 2 | void N::operator*(int) + // CHECK-N-NEXT: 3 | void N::operator*(short) + virtual void operator+(short); + virtual void operator*(short); + virtual void operator+(int); + virtual void operator*(int); +}; + +N n;