r232229 - MS ABI: Generate default constructor closures

David Majnemer david.majnemer at gmail.com
Fri Mar 13 15:36:55 PDT 2015


Author: majnemer
Date: Fri Mar 13 17:36:55 2015
New Revision: 232229

URL: http://llvm.org/viewvc/llvm-project?rev=232229&view=rev
Log:
MS ABI: Generate default constructor closures

The MS ABI utilizes a compiler generated function called the "vector
constructor iterator" to construct arrays of objects with
non-trivial constructors/destructors.  For this to work, the constructor
must follow a specific calling convention.  A thunk must be created if
the default constructor has default arguments, is variadic or is
otherwise incompatible.  This thunk is called the default constructor
closure.

N.B.  Default constructor closures are only generated if the default
constructor is exported because clang itself does not utilize vector
constructor iterators.  Failing to export the default constructor
closure will result in link/load failure if a translation unit compiled
with MSVC is on the import side.

Differential Revision: http://reviews.llvm.org/D8331

Modified:
    cfe/trunk/lib/AST/MicrosoftMangle.cpp
    cfe/trunk/lib/CodeGen/CGCall.cpp
    cfe/trunk/lib/CodeGen/CodeGenTypes.h
    cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/CodeGenCXX/dllexport.cpp

Modified: cfe/trunk/lib/AST/MicrosoftMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftMangle.cpp?rev=232229&r1=232228&r2=232229&view=diff
==============================================================================
--- cfe/trunk/lib/AST/MicrosoftMangle.cpp (original)
+++ cfe/trunk/lib/AST/MicrosoftMangle.cpp Fri Mar 13 17:36:55 2015
@@ -1640,10 +1640,11 @@ void MicrosoftCXXNameMangler::mangleFunc
                                    ->getPointeeType(),
                                /*SpelledAsLValue=*/true),
                            Range);
+        Out << '@';
       } else {
         llvm_unreachable("unexpected constructor closure!");
       }
-      Out << "@Z";
+      Out << 'Z';
       return;
     }
     Out << '@';

Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=232229&r1=232228&r2=232229&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCall.cpp Fri Mar 13 17:36:55 2015
@@ -347,12 +347,16 @@ CodeGenTypes::arrangeMSMemberPointerThun
 }
 
 const CGFunctionInfo &
-CodeGenTypes::arrangeMSCopyCtorClosure(const CXXConstructorDecl *CD) {
+CodeGenTypes::arrangeMSCtorClosure(const CXXConstructorDecl *CD,
+                                   CXXCtorType CT) {
+  assert(CT == Ctor_CopyingClosure || CT == Ctor_DefaultClosure);
+
   CanQual<FunctionProtoType> FTP = GetFormalType(CD);
   SmallVector<CanQualType, 2> ArgTys;
   const CXXRecordDecl *RD = CD->getParent();
   ArgTys.push_back(GetThisType(Context, RD));
-  ArgTys.push_back(*FTP->param_type_begin());
+  if (CT == Ctor_CopyingClosure)
+    ArgTys.push_back(*FTP->param_type_begin());
   if (RD->getNumVBases() > 0)
     ArgTys.push_back(Context.IntTy);
   CallingConv CC = Context.getDefaultCallingConvention(

Modified: cfe/trunk/lib/CodeGen/CodeGenTypes.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenTypes.h?rev=232229&r1=232228&r2=232229&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenTypes.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenTypes.h Fri Mar 13 17:36:55 2015
@@ -264,7 +264,8 @@ public:
                                              const FunctionProtoType *type,
                                              RequiredArgs required);
   const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD);
-  const CGFunctionInfo &arrangeMSCopyCtorClosure(const CXXConstructorDecl *CD);
+  const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD,
+                                                 CXXCtorType CT);
 
   const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionProtoType> Ty);
   const CGFunctionInfo &arrangeFreeFunctionType(CanQual<FunctionNoProtoType> Ty);

Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=232229&r1=232228&r2=232229&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Fri Mar 13 17:36:55 2015
@@ -635,7 +635,8 @@ public:
     return Fn;
   }
 
-  llvm::Function *getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD);
+  llvm::Function *getAddrOfCXXCtorClosure(const CXXConstructorDecl *CD,
+                                          CXXCtorType CT);
 
   llvm::Constant *getCatchableType(QualType T,
                                    uint32_t NVOffset = 0,
@@ -1060,9 +1061,29 @@ void MicrosoftCXXABI::initializeHiddenVi
   }
 }
 
+static bool hasDefaultCXXMethodCC(ASTContext &Context,
+                                  const CXXMethodDecl *MD) {
+  CallingConv ExpectedCallingConv = Context.getDefaultCallingConvention(
+      /*IsVariadic=*/false, /*IsCXXMethod=*/true);
+  CallingConv ActualCallingConv =
+      MD->getType()->getAs<FunctionProtoType>()->getCallConv();
+  return ExpectedCallingConv == ActualCallingConv;
+}
+
 void MicrosoftCXXABI::EmitCXXConstructors(const CXXConstructorDecl *D) {
   // There's only one constructor type in this ABI.
   CGM.EmitGlobal(GlobalDecl(D, Ctor_Complete));
+
+  // Exported default constructors either have a simple call-site where they use
+  // the typical calling convention and have a single 'this' pointer for an
+  // argument -or- they get a wrapper function which appropriately thunks to the
+  // real default constructor.  This thunk is the default constructor closure.
+  if (D->hasAttr<DLLExportAttr>() && D->isDefaultConstructor())
+    if (!hasDefaultCXXMethodCC(getContext(), D) || D->getNumParams() != 0) {
+      llvm::Function *Fn = getAddrOfCXXCtorClosure(D, Ctor_DefaultClosure);
+      Fn->setLinkage(llvm::GlobalValue::WeakODRLinkage);
+      Fn->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
+    }
 }
 
 void MicrosoftCXXABI::EmitVBPtrStores(CodeGenFunction &CGF,
@@ -3223,11 +3244,14 @@ void MicrosoftCXXABI::emitCXXStructor(co
 }
 
 llvm::Function *
-MicrosoftCXXABI::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
+MicrosoftCXXABI::getAddrOfCXXCtorClosure(const CXXConstructorDecl *CD,
+                                         CXXCtorType CT) {
+  assert(CT == Ctor_CopyingClosure || CT == Ctor_DefaultClosure);
+
   // Calculate the mangled name.
   SmallString<256> ThunkName;
   llvm::raw_svector_ostream Out(ThunkName);
-  getMangleContext().mangleCXXCtor(CD, Ctor_CopyingClosure, Out);
+  getMangleContext().mangleCXXCtor(CD, CT, Out);
   Out.flush();
 
   // If the thunk has been generated previously, just return it.
@@ -3235,12 +3259,13 @@ MicrosoftCXXABI::getAddrOfCXXCopyCtorClo
     return cast<llvm::Function>(GV);
 
   // Create the llvm::Function.
-  const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeMSCopyCtorClosure(CD);
+  const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeMSCtorClosure(CD, CT);
   llvm::FunctionType *ThunkTy = CGM.getTypes().GetFunctionType(FnInfo);
   const CXXRecordDecl *RD = CD->getParent();
   QualType RecordTy = getContext().getRecordType(RD);
   llvm::Function *ThunkFn = llvm::Function::Create(
       ThunkTy, getLinkageForRTTI(RecordTy), ThunkName.str(), &CGM.getModule());
+  bool IsCopy = CT == Ctor_CopyingClosure;
 
   // Start codegen.
   CodeGenFunction CGF(CGM);
@@ -3249,8 +3274,7 @@ MicrosoftCXXABI::getAddrOfCXXCopyCtorClo
   // Build FunctionArgs.
   FunctionArgList FunctionArgs;
 
-  // A copy constructor always starts with a 'this' pointer as its first
-  // argument.
+  // A constructor always starts with a 'this' pointer as its first argument.
   buildThisParam(CGF, FunctionArgs);
 
   // Following the 'this' pointer is a reference to the source object that we
@@ -3259,11 +3283,12 @@ MicrosoftCXXABI::getAddrOfCXXCopyCtorClo
       getContext(), nullptr, SourceLocation(), &getContext().Idents.get("src"),
       getContext().getLValueReferenceType(RecordTy,
                                           /*SpelledAsLValue=*/true));
-  FunctionArgs.push_back(&SrcParam);
+  if (IsCopy)
+    FunctionArgs.push_back(&SrcParam);
 
-  // Copy constructors for classes which utilize virtual bases have an
-  // additional parameter which indicates whether or not it is being delegated
-  // to by a more derived constructor.
+  // Constructors for classes which utilize virtual bases have an additional
+  // parameter which indicates whether or not it is being delegated to by a more
+  // derived constructor.
   ImplicitParamDecl IsMostDerived(getContext(), nullptr, SourceLocation(),
                                   &getContext().Idents.get("is_most_derived"),
                                   getContext().IntTy);
@@ -3278,7 +3303,8 @@ MicrosoftCXXABI::getAddrOfCXXCopyCtorClo
   llvm::Value *This = getThisValue(CGF);
 
   llvm::Value *SrcVal =
-      CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(&SrcParam), "src");
+      IsCopy ? CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(&SrcParam), "src")
+             : nullptr;
 
   CallArgList Args;
 
@@ -3286,11 +3312,12 @@ MicrosoftCXXABI::getAddrOfCXXCopyCtorClo
   Args.add(RValue::get(This), CD->getThisType(getContext()));
 
   // Push the src ptr.
-  Args.add(RValue::get(SrcVal), SrcParam.getType());
+  if (SrcVal)
+    Args.add(RValue::get(SrcVal), SrcParam.getType());
 
   // Add the rest of the default arguments.
   std::vector<Stmt *> ArgVec;
-  for (unsigned I = 1, E = CD->getNumParams(); I != E; ++I)
+  for (unsigned I = IsCopy ? 1 : 0, E = CD->getNumParams(); I != E; ++I)
     ArgVec.push_back(getContext().getDefaultArgExprForConstructor(CD, I));
 
   CodeGenFunction::RunCleanupsScope Cleanups(CGF);
@@ -3298,7 +3325,7 @@ MicrosoftCXXABI::getAddrOfCXXCopyCtorClo
   const auto *FPT = CD->getType()->castAs<FunctionProtoType>();
   ConstExprIterator ArgBegin(ArgVec.data()),
       ArgEnd(ArgVec.data() + ArgVec.size());
-  CGF.EmitCallArgs(Args, FPT, ArgBegin, ArgEnd, CD, 1);
+  CGF.EmitCallArgs(Args, FPT, ArgBegin, ArgEnd, CD, IsCopy ? 1 : 0);
 
   // Insert any ABI-specific implicit constructor arguments.
   unsigned ExtraArgs = addImplicitConstructorArgs(CGF, CD, Ctor_Complete,
@@ -3330,14 +3357,9 @@ llvm::Constant *MicrosoftCXXABI::getCatc
   const CXXConstructorDecl *CD =
       RD ? CGM.getContext().getCopyConstructorForExceptionObject(RD) : nullptr;
   CXXCtorType CT = Ctor_Complete;
-  if (CD) {
-    CallingConv ExpectedCallingConv = getContext().getDefaultCallingConvention(
-        /*IsVariadic=*/false, /*IsCXXMethod=*/true);
-    CallingConv ActualCallingConv =
-        CD->getType()->getAs<FunctionProtoType>()->getCallConv();
-    if (ExpectedCallingConv != ActualCallingConv || CD->getNumParams() != 1)
+  if (CD)
+    if (!hasDefaultCXXMethodCC(getContext(), CD) || CD->getNumParams() != 1)
       CT = Ctor_CopyingClosure;
-  }
 
   uint32_t Size = getContext().getTypeSizeInChars(T).getQuantity();
   SmallString<256> MangledName;
@@ -3358,7 +3380,7 @@ llvm::Constant *MicrosoftCXXABI::getCatc
   llvm::Constant *CopyCtor;
   if (CD) {
     if (CT == Ctor_CopyingClosure)
-      CopyCtor = getAddrOfCXXCopyCtorClosure(CD);
+      CopyCtor = getAddrOfCXXCtorClosure(CD, Ctor_CopyingClosure);
     else
       CopyCtor = CGM.getAddrOfCXXStructor(CD, StructorType::Complete);
 

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=232229&r1=232228&r2=232229&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Mar 13 17:36:55 2015
@@ -12064,6 +12064,24 @@ void Sema::ActOnStartCXXMemberDeclaratio
          "Broken injected-class-name");
 }
 
+static void getDefaultArgExprsForConstructors(Sema &S, CXXRecordDecl *Class) {
+  for (Decl *Member : Class->decls()) {
+    auto *CD = dyn_cast<CXXConstructorDecl>(Member);
+    if (!CD || !CD->isDefaultConstructor() || !CD->hasAttr<DLLExportAttr>())
+      continue;
+
+    for (unsigned I = 0, E = CD->getNumParams(); I != E; ++I) {
+      // Skip any default arguments that we've already instantiated.
+      if (S.Context.getDefaultArgExprForConstructor(CD, I))
+        continue;
+
+      Expr *DefaultArg = S.BuildCXXDefaultArgExpr(Class->getLocation(), CD,
+                                                  CD->getParamDecl(I)).get();
+      S.Context.addDefaultArgExprForConstructor(CD, I, DefaultArg);
+    }
+  }
+}
+
 void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
                                     SourceLocation RBraceLoc) {
   AdjustDeclIfTemplate(TagD);
@@ -12077,9 +12095,17 @@ void Sema::ActOnTagFinishDefinition(Scop
       RD->completeDefinition();
   }
 
-  if (isa<CXXRecordDecl>(Tag))
+  if (auto *Class = dyn_cast<CXXRecordDecl>(Tag)) {
     FieldCollector->FinishClass();
 
+    // Default constructors that are annotated with __declspec(dllexport) which
+    // have default arguments or don't use the standard calling convention are
+    // wrapped with a thunk called the default constructor closure.
+    if (!Class->getDescribedClassTemplate() &&
+        Context.getTargetInfo().getCXXABI().isMicrosoft())
+      getDefaultArgExprsForConstructors(*this, Class);
+  }
+
   // Exit this scope of this tag's definition.
   PopDeclContext();
 

Modified: cfe/trunk/test/CodeGenCXX/dllexport.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/dllexport.cpp?rev=232229&r1=232228&r2=232229&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/dllexport.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/dllexport.cpp Fri Mar 13 17:36:55 2015
@@ -484,6 +484,28 @@ struct S {
   };
 };
 
+struct CtorWithClosure {
+  __declspec(dllexport) CtorWithClosure(...) {}
+// M32-DAG: define weak_odr dllexport void @"\01??_FCtorWithClosure@@QAEXXZ"
+// M32-DAG:   %[[this_addr:.*]] = alloca %struct.CtorWithClosure*, align 4
+// M32-DAG:   store %struct.CtorWithClosure* %this, %struct.CtorWithClosure** %[[this_addr]], align 4
+// M32-DAG:   %[[this:.*]] = load %struct.CtorWithClosure*, %struct.CtorWithClosure** %[[this_addr]]
+// M32-DAG:   call %struct.CtorWithClosure* (%struct.CtorWithClosure*, ...)* @"\01??0CtorWithClosure@@QAA at ZZ"(%struct.CtorWithClosure* %[[this]])
+// M32-DAG:   ret void
+};
+
+struct __declspec(dllexport) ClassWithClosure {
+  ClassWithClosure(ClassWithClosure &&) = delete;
+  ClassWithClosure(ClassWithClosure &) = delete;
+  ~ClassWithClosure() = delete;
+  ClassWithClosure(...) {}
+// M32-DAG: define weak_odr dllexport void @"\01??_FClassWithClosure@@QAEXXZ"
+// M32-DAG:   %[[this_addr:.*]] = alloca %struct.ClassWithClosure*, align 4
+// M32-DAG:   store %struct.ClassWithClosure* %this, %struct.ClassWithClosure** %[[this_addr]], align 4
+// M32-DAG:   %[[this:.*]] = load %struct.ClassWithClosure*, %struct.ClassWithClosure** %[[this_addr]]
+// M32-DAG:   call %struct.ClassWithClosure* (%struct.ClassWithClosure*, ...)* @"\01??0ClassWithClosure@@QAA at ZZ"(%struct.ClassWithClosure* %[[this]])
+// M32-DAG:   ret void
+};
 
 struct __declspec(dllexport) T {
   // Copy assignment operator:





More information about the cfe-commits mailing list