Pulled out the thunk generation code into MicrosoftCXXABI.

Hi rnk, timurrrr,

http://llvm-reviews.chandlerc.com/D2104

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D2104?vs=5420&id=5489#toc

Files:
  include/clang/AST/Mangle.h
  lib/AST/MicrosoftMangle.cpp
  lib/CodeGen/CGVTables.cpp
  lib/CodeGen/MicrosoftCXXABI.cpp
  test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
Index: include/clang/AST/Mangle.h
===================================================================
--- include/clang/AST/Mangle.h
+++ include/clang/AST/Mangle.h
@@ -193,6 +193,9 @@
                                 ArrayRef<const CXXRecordDecl *> BasePath,
                                 raw_ostream &Out) = 0;
 
+  virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD,
+                                        int OffsetInVFTable, raw_ostream &) = 0;
+
   static bool classof(const MangleContext *C) {
     return C->getKind() == MK_Microsoft;
   }
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -181,6 +181,8 @@
       : MicrosoftMangleContext(Context, Diags) {}
   virtual bool shouldMangleCXXName(const NamedDecl *D);
   virtual void mangleCXXName(const NamedDecl *D, raw_ostream &Out);
+  virtual void mangleVirtualMemPtrThunk(const CXXMethodDecl *MD,
+                                        int OffsetInVFTable, raw_ostream &);
   virtual void mangleThunk(const CXXMethodDecl *MD,
                            const ThunkInfo &Thunk,
                            raw_ostream &);
@@ -1921,6 +1923,19 @@
   }
 }
 
+void MicrosoftMangleContextImpl::mangleVirtualMemPtrThunk(
+    const CXXMethodDecl *MD, int OffsetInVFTable, raw_ostream &Out) {
+  bool Is64Bit = getASTContext().getTargetInfo().getPointerWidth(0) == 64;
+
+  MicrosoftCXXNameMangler Mangler(*this, Out);
+  Mangler.getStream() << "\01??_9";
+  Mangler.mangleName(MD->getParent());
+  Mangler.getStream() << "$B";
+  Mangler.mangleNumber(OffsetInVFTable);
+  Mangler.getStream() << "A";
+  Mangler.getStream() << (Is64Bit ? "A" : "E");
+}
+
 void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
                                              const ThunkInfo &Thunk,
                                              raw_ostream &Out) {
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -253,6 +253,7 @@
   // CodeGenFunction::GenerateCode.
 
   // Create the implicit 'this' parameter declaration.
+  assert(!CurGD.getDecl() && "CurGD was already set!");
   CurGD = GD;
   CGM.getCXXABI().BuildInstanceFunctionParams(*this, ResultType, FunctionArgs);
 
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -351,6 +351,10 @@
                                   llvm::Value *MemPtr,
                                   const MemberPointerType *MPT);
 
+  /// \brief Generate a thunk for calling a virtual member function MD.
+  llvm::Function *EmitVirtualMemPtrThunk(const CXXMethodDecl *MD,
+                                         StringRef ThunkName);
+
 private:
   typedef std::pair<const CXXRecordDecl *, CharUnits> VFTableIdTy;
   typedef llvm::DenseMap<VFTableIdTy, llvm::GlobalVariable *> VFTablesMapTy;
@@ -1370,12 +1374,7 @@
   CodeGenTypes &Types = CGM.getTypes();
 
   llvm::Constant *FirstField;
-  if (MD->isVirtual()) {
-    // FIXME: We have to instantiate a thunk that loads the vftable and jumps to
-    // the right offset.
-    CGM.ErrorUnsupported(MD, "pointer to virtual member function");
-    FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy);
-  } else {
+  if (!MD->isVirtual()) {
     const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
     llvm::Type *Ty;
     // Check whether the function has a computable LLVM signature.
@@ -1389,6 +1388,35 @@
     }
     FirstField = CGM.GetAddrOfFunction(MD, Ty);
     FirstField = llvm::ConstantExpr::getBitCast(FirstField, CGM.VoidPtrTy);
+  } else {
+    if (MD->isVariadic()) {
+      CGM.ErrorUnsupported(MD, "pointer to variadic virtual member function");
+      FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy);
+    } else if (!CGM.getTypes().isFuncTypeConvertible(
+                    MD->getType()->castAs<FunctionType>())) {
+      CGM.ErrorUnsupported(MD, "pointer to virtual member function with "
+                               "incomplete return or parameter type");
+      FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy);
+    } else if (CGM.getMicrosoftVTableContext()
+                   .getMethodVFTableLocation(MD)
+                   .VBase) {
+      CGM.ErrorUnsupported(MD, "pointer to virtual member function overriding "
+                               "member function in virtual base class");
+      FirstField = llvm::Constant::getNullValue(CGM.VoidPtrTy);
+    } else {
+      MicrosoftVTableContext::MethodVFTableLocation ML =
+          CGM.getMicrosoftVTableContext().getMethodVFTableLocation(MD);
+
+      SmallString<256> ThunkName;
+      bool Is64Bit = getContext().getTargetInfo().getPointerWidth(0) == 64;
+      int OffsetInVFTable = ML.Index * (Is64Bit ? 8 : 4);
+      llvm::raw_svector_ostream Out(ThunkName);
+      getMangleContext().mangleVirtualMemPtrThunk(MD, OffsetInVFTable, Out);
+      Out.flush();
+
+      llvm::Function *Thunk = EmitVirtualMemPtrThunk(MD, ThunkName.str());
+      FirstField = llvm::ConstantExpr::getBitCast(Thunk, CGM.VoidPtrTy);
+    }
   }
 
   // The rest of the fields are common with data member pointers.
@@ -1872,7 +1900,80 @@
   return Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo());
 }
 
+llvm::Function *
+MicrosoftCXXABI::EmitVirtualMemPtrThunk(const CXXMethodDecl *MD,
+                                        StringRef ThunkName) {
+  // If the thunk has been generated previously, just return it.
+  if (llvm::GlobalValue *GV = CGM.getModule().getNamedValue(ThunkName))
+    return cast<llvm::Function>(GV);
+
+  // Create the llvm::Function.
+  const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(MD);
+  llvm::FunctionType *ThunkTy = CGM.getTypes().GetFunctionType(FnInfo);
+  llvm::Function *ThunkFn =
+      llvm::Function::Create(ThunkTy, llvm::Function::ExternalLinkage,
+                             ThunkName.str(), &CGM.getModule());
+  assert(ThunkFn->getName() == ThunkName && "name was uniqued!");
+  ThunkFn->setLinkage(llvm::GlobalValue::LinkOnceODRLinkage);
+  CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn);
+  CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn);
+
+  CodeGenFunction CGF(CGM);
+  CGF.CurGD = MD;
+
+  // Build FunctionArgs.
+  FunctionArgList FunctionArgs;
+  const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
+  QualType ResultType = FPT->getResultType();
+  BuildInstanceFunctionParams(CGF, ResultType, FunctionArgs);
+  for (FunctionDecl::param_const_iterator I = MD->param_begin(),
+                                          E = MD->param_end();
+       I != E; ++I)
+    FunctionArgs.push_back(*I);
+
+  // Start defining the function.
+  CGF.StartFunction(GlobalDecl(), ResultType, ThunkFn, FnInfo, FunctionArgs,
+                    SourceLocation());
+  EmitInstanceFunctionProlog(CGF);
+
+  // Get the 'this' pointer.
+  llvm::Value *This = getThisValue(CGF);
+
+  // Get to the Callee in the ABI-specific way.
+  llvm::Value *Callee = getVirtualFunctionPointer(CGF, MD, This, ThunkTy);
+
+  // Build call arguments.
+  CallArgList CallArgs;
+  QualType ThisType = MD->getThisType(getContext());
+  CallArgs.add(RValue::get(This), ThisType);
+  for (FunctionDecl::param_const_iterator I = MD->param_begin(),
+                                          E = MD->param_end();
+       I != E; ++I)
+    CGF.EmitDelegateCallArg(CallArgs, *I, (*I)->getLocStart());
+
+  // Determine whether we have a return value slot to use.
+  ReturnValueSlot Slot;
+  if (!ResultType->isVoidType() &&
+      FnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect &&
+      !CGF.hasScalarEvaluationKind(FnInfo.getReturnType()))
+    Slot = ReturnValueSlot(CGF.ReturnValue, ResultType.isVolatileQualified());
+
+  // Emit the call.
+  RValue RV = CGF.EmitCall(FnInfo, Callee, Slot, CallArgs, MD);
+
+  // Note: we don't do any return adjustment. The callee should be doing that if
+  // necessary.
+
+  // Emit return.
+  if (!ResultType->isVoidType() && Slot.isNull())
+    EmitReturnFromThunk(CGF, RV, ResultType);
+
+  CGF.FinishFunction();
+  CGM.setFunctionLinkage(MD, ThunkFn); // XXX: Do we need this?
+
+  return ThunkFn;
+}
+
 CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
   return new MicrosoftCXXABI(CGM);
 }
-
Index: test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/microsoft-abi-virtual-member-pointers.cpp
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -fno-rtti -emit-llvm -cxx-abi microsoft -triple=i386-pc-win32 %s -o - | FileCheck %s --check-prefix=CHECK32
+// RUN: %clang_cc1 -fno-rtti -emit-llvm -cxx-abi microsoft -triple=x86_64-pc-win32 %s -o - | FileCheck %s --check-prefix=CHECK64
+
+struct S {
+  int x, y, z;
+};
+
+struct C {
+  virtual void foo();
+  virtual int bar(int, double);
+  virtual S baz(int);
+};
+
+void f() {
+  void (C::*ptr)();
+  ptr = &C::foo;
+  ptr = &C::foo; // Don't crash trying to define the thunk twice :)
+
+  int (C::*ptr2)(int, double);
+  ptr2 = &C::bar;
+
+  S (C::*ptr3)(int);
+  ptr3 = &C::baz;
+
+// CHECK32-LABEL: define void @"\01?f@@YAXXZ"()
+// CHECK32: store i8* bitcast (void (%struct.C*)* @"\01??_9C@@$BA@AE" to i8*), i8** %ptr
+// CHECK32: store i8* bitcast (i32 (%struct.C*, i32, double)* @"\01??_9C@@$B3AE" to i8*), i8** %ptr2
+// CHECK32: store i8* bitcast (void (%struct.S*, %struct.C*, i32)* @"\01??_9C@@$B7AE" to i8*), i8** %ptr3
+// CHECK32: }
+//
+// CHECK64-LABEL: define void @"\01?f@@YAXXZ"()
+// CHECK64: store i8* bitcast (void (%struct.C*)* @"\01??_9C@@$BA@AA" to i8*), i8** %ptr
+// CHECK64: store i8* bitcast (i32 (%struct.C*, i32, double)* @"\01??_9C@@$B7AA" to i8*), i8** %ptr2
+// CHECK64: store i8* bitcast (void (%struct.S*, %struct.C*, i32)* @"\01??_9C@@$BBA@AA" to i8*), i8** %ptr3
+// CHECK64: }
+}
+
+// Thunk for calling the 1st virtual function in C with no parameters.
+// CHECK32-LABEL: define x86_thiscallcc void @"\01??_9C@@$BA@AE"(%struct.C* %this) unnamed_addr
+// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*)** %{{.*}}, i64 0
+// CHECK32: [[CALLEE:%.*]] = load void (%struct.C*)** [[VPTR]]
+// CHECK32: call x86_thiscallcc void [[CALLEE]](%struct.C* %{{.*}})
+// CHECK32: ret void
+// CHECK32: }
+//
+// CHECK64-LABEL: define void @"\01??_9C@@$BA@AA"(%struct.C* %this) unnamed_addr
+// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.C*)** %{{.*}}, i64 0
+// CHECK64: [[CALLEE:%.*]] = load void (%struct.C*)** [[VPTR]]
+// CHECK64: call void [[CALLEE]](%struct.C* %{{.*}})
+// CHECK64: ret void
+// CHECK64: }
+
+// Thunk for calling the 2nd virtual function in C, taking int and double as parameters, returning int.
+// CHECK32-LABEL: define x86_thiscallcc i32 @"\01??_9C@@$B3AE"(%struct.C* %this, i32, double) unnamed_addr
+// CHECK32: [[VPTR:%.*]] = getelementptr inbounds i32 (%struct.C*, i32, double)** %{{.*}}, i64 1
+// CHECK32: [[CALLEE:%.*]] = load i32 (%struct.C*, i32, double)** [[VPTR]]
+// CHECK32: [[CALL:%.*]] = call x86_thiscallcc i32 [[CALLEE]](%struct.C* %{{.*}}, i32 %{{.*}}, double %{{.*}})
+// CHECK32: ret i32 [[CALL]]
+// CHECK32: }
+//
+// CHECK64-LABEL: define i32 @"\01??_9C@@$B7AA"(%struct.C* %this, i32, double) unnamed_addr
+// CHECK64: [[VPTR:%.*]] = getelementptr inbounds i32 (%struct.C*, i32, double)** %{{.*}}, i64 1
+// CHECK64: [[CALLEE:%.*]] = load i32 (%struct.C*, i32, double)** [[VPTR]]
+// CHECK64: [[CALL:%.*]] = call i32 [[CALLEE]](%struct.C* %{{.*}}, i32 %{{.*}}, double %{{.*}})
+// CHECK64: ret i32 [[CALL]]
+// CHECK64: }
+
+// Thunk for calling the 3rd virtual function in C, taking an int parameter, returning a struct.
+// CHECK32-LABEL: define x86_thiscallcc void @"\01??_9C@@$B7AE"(%struct.S* noalias sret %agg.result, %struct.C* %this, i32) unnamed_addr
+// CHECK32: [[VPTR:%.*]] = getelementptr inbounds void (%struct.S*, %struct.C*, i32)** %{{.*}}, i64 2
+// CHECK32: [[CALLEE:%.*]] = load void (%struct.S*, %struct.C*, i32)** [[VPTR]]
+// CHECK32: call x86_thiscallcc void [[CALLEE]](%struct.S* sret %agg.result, %struct.C* %{{.*}}, i32 %{{.*}})
+// CHECK32: ret void
+// CHECK32: }
+//
+// CHECK64-LABEL: define void @"\01??_9C@@$BBA@AA"(%struct.S* noalias sret %agg.result, %struct.C* %this, i32) unnamed_addr
+// CHECK64: [[VPTR:%.*]] = getelementptr inbounds void (%struct.S*, %struct.C*, i32)** %{{.*}}, i64 2
+// CHECK64: [[CALLEE:%.*]] = load void (%struct.S*, %struct.C*, i32)** [[VPTR]]
+// CHECK64: call void [[CALLEE]](%struct.S* sret %agg.result, %struct.C* %{{.*}}, i32 %{{.*}})
+// CHECK64: ret void
+// CHECK64: }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to