[prev in list] [next in list] [prev in thread] [next in thread] 

List:       cfe-commits
Subject:    [PATCH] Optimize pointers to member function
From:       Olivier Goffart <ogoffart () kde ! org>
Date:       2013-02-14 11:57:42
Message-ID: 1582805.juW4tgnMeq () gargamel
[Download RAW message or body]

Hi,

This patch optimize comparison and calling of pointers to member function.

If we know that a given class do not have multiple inheritance, we can 
simplify the comparison and the calls as the pointer to this will never be 
shifted.
On the Microsoft ABI (which is not implemented in clang), the size of a 
pointer to a member function is even reduced in that case.[1]

Similarly, if we know the class has no virtual table, no need to emit a branch 
for virtual functions.

[1] http://blogs.msdn.com/b/oldnewthing/archive/2004/02/09/70002.aspx
["0001-Optimize-pointers-to-member-function.patch" (0001-Optimize-pointers-to-member-function.patch)]

From 6ed5c2d5e62dfc1a78f63ab877e328483e63a959 Mon Sep 17 00:00:00 2001
From: Olivier Goffart <ogoffart@woboq.com>
Date: Sat, 8 Dec 2012 19:23:45 +0100
Subject: [PATCH] Optimize pointers to member function

If we know the class is only using single inheritence, we need not to
generate the code that will adjust the 'this' pointer.
If we know the class do not have virtual functions, we need not to
generate the code that checks and emit virtual calls

No need to compare the adjustment if we now there is ingle inheristence.
We still need to do it on ARM because the virtual flag is with the
adjustment
---
 lib/CodeGen/ItaniumCXXABI.cpp                    |  64 +++++++++---
 test/CodeGenCXX/pointers-to-member-functions.cpp | 119 +++++++++++++++++++++++
 2 files changed, 170 insertions(+), 13 deletions(-)
 create mode 100644 test/CodeGenCXX/pointers-to-member-functions.cpp

diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp
index a3d8cec..66c55cc 100644
--- a/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/lib/CodeGen/ItaniumCXXABI.cpp
@@ -239,30 +239,51 @@ ItaniumCXXABI::EmitLoadOfMemberFunctionPointer(CodeGenFunction \
&CGF,  CGM.getTypes().GetFunctionType(
       CGM.getTypes().arrangeCXXMethodType(RD, FPT));
 
+  bool HasDefinition = RD->hasDefinition();
+  // If we know the class does not use multiple inheritance, no need to adjust \
'this' +  bool IsSimpleInheritence = false;
+  if (HasDefinition) {
+    const CXXRecordDecl* I = RD;
+    while (I->getNumBases() == 1)
+      I = I->bases_begin()->getType()->getAsCXXRecordDecl();
+    if (I->getNumBases() == 0)
+      IsSimpleInheritence = true;
+  }
+
+  bool MightBePolymorphic = !HasDefinition || RD->isPolymorphic();
+
   llvm::IntegerType *ptrdiff = getPtrDiffTy();
   llvm::Constant *ptrdiff_1 = llvm::ConstantInt::get(ptrdiff, 1);
 
-  llvm::BasicBlock *FnVirtual = CGF.createBasicBlock("memptr.virtual");
-  llvm::BasicBlock *FnNonVirtual = CGF.createBasicBlock("memptr.nonvirtual");
-  llvm::BasicBlock *FnEnd = CGF.createBasicBlock("memptr.end");
-
   // Extract memptr.adj, which is in the second field.
-  llvm::Value *RawAdj = Builder.CreateExtractValue(MemFnPtr, 1, "memptr.adj");
+  llvm::Value *RawAdj;
+  if (!IsSimpleInheritence || (IsARM && MightBePolymorphic))
+      RawAdj = Builder.CreateExtractValue(MemFnPtr, 1, "memptr.adj");
 
   // Compute the true adjustment.
   llvm::Value *Adj = RawAdj;
-  if (IsARM)
+  if (IsARM && MightBePolymorphic)
     Adj = Builder.CreateAShr(Adj, ptrdiff_1, "memptr.adj.shifted");
 
-  // Apply the adjustment and cast back to the original struct type
-  // for consistency.
-  llvm::Value *Ptr = Builder.CreateBitCast(This, Builder.getInt8PtrTy());
-  Ptr = Builder.CreateInBoundsGEP(Ptr, Adj);
-  This = Builder.CreateBitCast(Ptr, This->getType(), "this.adjusted");
-  
+  if (!IsSimpleInheritence) {
+    // Apply the adjustment and cast back to the original struct type
+    // for consistency.
+    llvm::Value *Ptr = Builder.CreateBitCast(This, Builder.getInt8PtrTy());
+    Ptr = Builder.CreateInBoundsGEP(Ptr, Adj);
+    This = Builder.CreateBitCast(Ptr, This->getType(), "this.adjusted");
+  }
+
   // Load the function pointer.
   llvm::Value *FnAsInt = Builder.CreateExtractValue(MemFnPtr, 0, "memptr.ptr");
-  
+
+  if (!MightBePolymorphic) {
+      return Builder.CreateIntToPtr(FnAsInt, FTy->getPointerTo(), \
"memptr.nonvirtualfn"); +  }
+
+  llvm::BasicBlock *FnVirtual = CGF.createBasicBlock("memptr.virtual");
+  llvm::BasicBlock *FnNonVirtual = CGF.createBasicBlock("memptr.nonvirtual");
+  llvm::BasicBlock *FnEnd = CGF.createBasicBlock("memptr.end");
+
   // If the LSB in the function pointer is 1, the function pointer points to
   // a virtual function.
   llvm::Value *IsVirtual;
@@ -623,6 +644,23 @@ ItaniumCXXABI::EmitMemberPointerComparison(CodeGenFunction &CGF,
   // true for equality to hold.
   llvm::Value *PtrEq = Builder.CreateICmp(Eq, LPtr, RPtr, "cmp.ptr");
 
+  if (!IsARM) {
+    const CXXRecordDecl *RD =
+      cast<CXXRecordDecl>(MPT->getClass()->getAs<RecordType>()->getDecl());
+
+    if (RD->hasDefinition()) {
+      // If we know the class does not use multiple inheritance, no need to
+      // compare the adjustment
+      const CXXRecordDecl* I = RD;
+      while (I->getNumBases() == 1)
+        I = I->bases_begin()->getType()->getAsCXXRecordDecl();
+      if (I->getNumBases() == 0) {
+          // We are done
+          return PtrEq;
+      }
+    }
+  }
+
   // This condition, together with the assumption that L.ptr == R.ptr,
   // tests whether the pointers are both null.  ARM imposes an extra
   // condition.
diff --git a/test/CodeGenCXX/pointers-to-member-functions.cpp \
b/test/CodeGenCXX/pointers-to-member-functions.cpp new file mode 100644
index 0000000..9960c95
--- /dev/null
+++ b/test/CodeGenCXX/pointers-to-member-functions.cpp
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 %s -emit-llvm -triple=x86_64-apple-darwin10 -o - | FileCheck %s
+
+
+struct A { int f1() { return 1; } };
+struct B { int f2() { return 2; } };
+struct C : A { };
+struct V { virtual int f3() { return 3; } };
+struct D : A, B { };
+struct E : V { int f3() { return 4; } };
+struct F : D {};
+struct G : D, E {};
+struct Z;
+
+namespace Call {
+  int callA(int(A::*f)(), A*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callAEM1AFivEPS0_
+  //CHECK-NOT: getelementptr inbounds
+  //CHECK-NOT: br i1 %memptr.isvirtual, label %memptr.virtual, label \
%memptr.nonvirtual +  //CHECK: call i32 %memptr.nonvirtualfn
+
+  int callC(int(C::*f)(), C*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callCEM1CFivEPS0_
+  //CHECK-NOT: getelementptr inbounds
+  //CHECK-NOT: br i1 %memptr.isvirtual, label %memptr.virtual, label \
%memptr.nonvirtual +  //CHECK: call i32 %memptr.nonvirtualfn
+
+  int callD(int(D::*f)(), D*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callDEM1DFivEPS0_
+  //CHECK: getelementptr inbounds
+  //CHECK-NOT: br i1 %memptr.isvirtual, label %memptr.virtual, label \
%memptr.nonvirtual +  //CHECK: call i32 %memptr.nonvirtualfn
+
+  int callE(int(E::*f)(), E*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callEEM1EFivEPS0_
+  //CHECK-NOT: getelementptr inbounds
+  //CHECK: br i1 %memptr.isvirtual, label %memptr.virtual, label %memptr.nonvirtual
+  //CHECK: call i32
+
+  int callF(int(F::*f)(), F*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callFEM1FFivEPS0_
+  //CHECK: getelementptr inbounds
+  //CHECK-NOT: br i1 %memptr.isvirtual, label %memptr.virtual, label \
%memptr.nonvirtual +  //CHECK: call i32 %memptr.nonvirtualfn
+
+  int callG(int(G::*f)(), G*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callGEM1GFivEPS0_
+  //CHECK: getelementptr inbounds
+  //CHECK: br i1 %memptr.isvirtual, label %memptr.virtual, label %memptr.nonvirtual
+  //CHECK: call i32
+
+  int callZ(int(Z::*f)(), Z*t) { return (t->*f)(); }
+  //CHECK: @_ZN4Call5callZEM1ZFivEPS0_
+  //CHECK: getelementptr inbounds
+  //CHECK: br i1 %memptr.isvirtual, label %memptr.virtual, label %memptr.nonvirtual
+  //CHECK: call i32
+}
+
+namespace Compare {
+  bool cmpEqA(int(A::*rhs)(), int(A::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare6cmpEqAEM1AFivES2_
+  //CHECK: icmp eq i64
+  //CHECK-NOT: icmp eq i64
+  //CHECK-NOT: and i1
+  //CHECK-NOT: or i1
+  //CHECK: ret i1
+
+  bool cmpEqC(int(C::*rhs)(), int(C::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare6cmpEqCEM1CFivES2_
+  //CHECK: icmp eq i64
+  //CHECK-NOT: icmp eq i64
+  //CHECK-NOT: and i1
+  //CHECK-NOT: or i1
+  //CHECK: ret i1
+
+  bool cmpEqF(int(F::*rhs)(), int(F::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare6cmpEqFEM1FFivES2_
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: or i1
+  //CHECK: and i1
+  //CHECK: ret i1
+
+  bool cmpEqG(int(G::*rhs)(), int(G::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare6cmpEqGEM1GFivES2_
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: or i1
+  //CHECK: and i1
+  //CHECK: ret i1
+
+  bool cmpEqZ(int(Z::*rhs)(), int(Z::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare6cmpEqZEM1ZFivES2_
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: or i1
+  //CHECK: and i1
+  //CHECK: ret i1
+
+  bool cmpNeqC(int(C::*rhs)(), int(C::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare7cmpNeqCEM1CFivES2_
+  //CHECK: icmp eq i64
+  //CHECK-NOT: icmp eq i64
+  //CHECK-NOT: and i1
+  //CHECK-NOT: or i1
+  //CHECK: ret i1
+
+  bool cmpNeqG(int(G::*rhs)(), int(G::*lhs)()) { return rhs == lhs; }
+  //CHECK: @_ZN7Compare7cmpNeqGEM1GFivES2_
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: icmp eq i64
+  //CHECK: or i1
+  //CHECK: and i1
+  //CHECK: ret i1
+}
+
-- 
1.7.12.1



_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic