]> granicus.if.org Git - clang/commitdiff
MS ABI: Generate default constructor closures
authorDavid Majnemer <david.majnemer@gmail.com>
Fri, 13 Mar 2015 22:36:55 +0000 (22:36 +0000)
committerDavid Majnemer <david.majnemer@gmail.com>
Fri, 13 Mar 2015 22:36:55 +0000 (22:36 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@232229 91177308-0d34-0410-b5e6-96231b3b80d8

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

index f56da26a09bb9470b667d6c9d8b6601b4cb1ac6e..6a0ebda7b33fa1028b9b1f5f33eb9f33596cf1b4 100644 (file)
@@ -1640,10 +1640,11 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
                                    ->getPointeeType(),
                                /*SpelledAsLValue=*/true),
                            Range);
+        Out << '@';
       } else {
         llvm_unreachable("unexpected constructor closure!");
       }
-      Out << "@Z";
+      Out << 'Z';
       return;
     }
     Out << '@';
index 678496421184072a4ff443d3a086ba72488f9678..3cb72bd1512a096a75524e10ba4604a2b812d9fc 100644 (file)
@@ -347,12 +347,16 @@ CodeGenTypes::arrangeMSMemberPointerThunk(const CXXMethodDecl *MD) {
 }
 
 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(
index f9cd3837fe9d29541d760145cc4c1c6de86b7c26..26d37f3c2401e9e99bce50da04179331fef5d4fe 100644 (file)
@@ -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);
index 6b53359f28b9d7f4ac434092c695814092148812..96fcb2f8dbe24f7124c34bd32a36bab944e7827d 100644 (file)
@@ -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::initializeHiddenVirtualInheritanceMembers(
   }
 }
 
+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(const CXXMethodDecl *MD,
 }
 
 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::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
     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::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
   // 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::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
       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::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
   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::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
   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::getAddrOfCXXCopyCtorClosure(const CXXConstructorDecl *CD) {
   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::getCatchableType(QualType T,
   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::getCatchableType(QualType T,
   llvm::Constant *CopyCtor;
   if (CD) {
     if (CT == Ctor_CopyingClosure)
-      CopyCtor = getAddrOfCXXCopyCtorClosure(CD);
+      CopyCtor = getAddrOfCXXCtorClosure(CD, Ctor_CopyingClosure);
     else
       CopyCtor = CGM.getAddrOfCXXStructor(CD, StructorType::Complete);
 
index 9c6f638119b0cf001f8c36299caf0de94807cdc4..7a70cddf9d0afc14eb532f784296c0f3e3bc255f 100644 (file)
@@ -12064,6 +12064,24 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD,
          "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(Scope *S, Decl *TagD,
       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();
 
index 3b04c264c5db9c16237bb38bc96003e7a19002b3..60d37ef0a87ad3f87f69db953384e93b1fc53526 100644 (file)
@@ -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@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@ZZ"(%struct.ClassWithClosure* %[[this]])
+// M32-DAG:   ret void
+};
 
 struct __declspec(dllexport) T {
   // Copy assignment operator: