Hi rnk, nrieck,
This implements the central part of support for dllimport/dllexport on classes:
allowing the attribute on class declarations, inheriting it to class members,
and forcing emission of exported members. It's based on Nico Rieck's patch from
http://reviews.llvm.org/D1099.
This patch doesn't propagate dllexport to bases that are template
specializations, which is an interesting problem. It also doesn't look at the
rules when redeclaring classes with different attributes, I'd like to do that
separately.
This patch depends on my patch in http://reviews.llvm.org/D3809. Please take a
look!
http://reviews.llvm.org/D3877
Files:
include/clang/Basic/Attr.td
lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGVTables.cpp
lib/CodeGen/CodeGenModule.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaDeclCXX.cpp
test/CodeGenCXX/dllexport.cpp
test/CodeGenCXX/dllimport.cpp
test/SemaCXX/dllexport.cpp
test/SemaCXX/dllimport.cpp
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1659,14 +1659,14 @@
def DLLExport : InheritableAttr, TargetSpecificAttr<TargetWindows> {
let Spellings = [Declspec<"dllexport">, GCC<"dllexport">];
- let Subjects = SubjectList<[Function, Var]>;
+ let Subjects = SubjectList<[Function, Var, CXXRecord]>;
let Documentation = [Undocumented];
}
def DLLImport : InheritableAttr, TargetSpecificAttr<TargetWindows> {
let Spellings = [Declspec<"dllimport">, GCC<"dllimport">];
- // Technically, the subjects for DllImport are Function and Var, but there is
- // custom semantic handling required when MicrosoftExt is true.
+ // Technically, the subjects for DllImport are Function, Var and CXXRecord,
+ // but there is custom semantic handling required when MicrosoftExt is true.
let Documentation = [Undocumented];
}
Index: lib/CodeGen/CGCXX.cpp
===================================================================
--- lib/CodeGen/CGCXX.cpp
+++ lib/CodeGen/CGCXX.cpp
@@ -44,6 +44,10 @@
if (!D->hasTrivialBody())
return true;
+ // For exported destructors, we need a full definition.
+ if (D->hasAttr<DLLExportAttr>())
+ return true;
+
const CXXRecordDecl *Class = D->getParent();
// If we need to manipulate a VTT parameter, give up.
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -651,18 +651,31 @@
// internal linkage.
if (Context.getLangOpts().AppleKext)
return llvm::Function::InternalLinkage;
-
+
+ llvm::GlobalVariable::LinkageTypes DiscardableODRLinkage =
+ llvm::GlobalValue::LinkOnceODRLinkage;
+ llvm::GlobalVariable::LinkageTypes NonDiscardableODRLinkage =
+ llvm::GlobalValue::WeakODRLinkage;
+ if (RD->hasAttr<DLLExportAttr>()) {
+ // Cannot discard exported functions.
+ DiscardableODRLinkage = NonDiscardableODRLinkage;
+ } else if (RD->hasAttr<DLLImportAttr>()) {
+ // Imported functions are available externally.
+ DiscardableODRLinkage = llvm::GlobalVariable::AvailableExternallyLinkage;
+ NonDiscardableODRLinkage = llvm::GlobalVariable::AvailableExternallyLinkage;
+ }
+
switch (RD->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
case TSK_ImplicitInstantiation:
- return llvm::GlobalVariable::LinkOnceODRLinkage;
+ return DiscardableODRLinkage;
case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable("Should not have been asked to emit this");
case TSK_ExplicitInstantiationDefinition:
- return llvm::GlobalVariable::WeakODRLinkage;
+ return NonDiscardableODRLinkage;
}
llvm_unreachable("Invalid TemplateSpecializationKind!");
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -1389,6 +1389,13 @@
B));
}
+ if (const auto *Dtor = dyn_cast_or_null<CXXDestructorDecl>(D)) {
+ if (getCXXABI().useThunkForDtorVariant(Dtor, GD.getDtorType())) {
+ // Don't dllexport/import destructor thunks.
+ F->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
+ }
+ }
+
if (!DontDefer) {
// All MSVC dtors other than the base dtor are linkonce_odr and delegate to
// each other bottoming out with the base dtor. Therefore we emit non-base
Index: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -912,6 +912,12 @@
VTable->setInitializer(Init);
VTable->setLinkage(Linkage);
+
+ if (RD->hasAttr<DLLImportAttr>())
+ VTable->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
+ else if (RD->hasAttr<DLLExportAttr>())
+ VTable->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
+
CGM.setGlobalVisibility(VTable, RD);
}
}
@@ -1217,6 +1223,11 @@
llvm::Constant *Init = llvm::ConstantArray::get(VBTableType, Offsets);
GV->setInitializer(Init);
+ if (RD->hasAttr<DLLImportAttr>())
+ GV->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
+ else if (RD->hasAttr<DLLExportAttr>())
+ GV->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass);
+
// Set the right visibility.
CGM.setGlobalVisibility(GV, RD);
}
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -3836,15 +3836,19 @@
}
static void handleDLLImportAttr(Sema &S, Decl *D, const AttributeList &Attr) {
- // Attribute can be applied only to functions or variables.
- FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
- if (!FD && !isa<VarDecl>(D)) {
+ // Attribute can be applied only to functions, variables or classes.
+ if (!isa<VarDecl>(D) && !isa<FunctionDecl>(D) && !isa<CXXRecordDecl>(D)) {
// Apparently Visual C++ thinks it is okay to not emit a warning
// in this case, so only emit a warning when -fms-extensions is not
// specified.
- if (!S.getLangOpts().MicrosoftExt)
- S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
- << Attr.getName() << ExpectedVariableOrFunction;
+ if (S.getLangOpts().MicrosoftExt)
+ return;
+
+ unsigned Expected = S.getLangOpts().CPlusPlus
+ ? ExpectedFunctionVariableOrClass
+ : ExpectedVariableOrFunction;
+ S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
+ << Attr.getName() << Expected;
return;
}
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -4346,6 +4346,78 @@
}
}
+/// \brief Get the dllimport/dllexport attribute from a declaration.
+static Attr *getDLLAttr(Decl *D) {
+ assert(!(D->hasAttr<DLLImportAttr>() && D->hasAttr<DLLExportAttr>()) &&
+ "A declaration cannot be both dllimport and dllexport.");
+ if (auto *Import = D->getAttr<DLLImportAttr>())
+ return Import;
+ if (auto *Export = D->getAttr<DLLExportAttr>())
+ return Export;
+ return nullptr;
+}
+
+/// \brief Check dllimport/dllexport class member.
+static void checkDLLClassMember(Sema &S, Decl *Member, Attr *ClassAttr,
+ CXXRecordDecl *ReturnedRecord) {
+ if (getDLLAttr(Member)) {
+ // FIXME: Error about importing/exporting individual members.
+ return;
+ }
+
+ bool ClassExported = ClassAttr->getKind() == attr::DLLExport;
+
+ if (ClassExported && ReturnedRecord &&
+ !ReturnedRecord->hasAttr<DLLExportAttr>()) {
+ // FIXME: Warn: the returned class should probably be exported.
+ }
+
+ auto *NewAttr = cast<InheritableAttr>(ClassAttr->clone(S.getASTContext()));
+ NewAttr->setInherited(true);
+ Member->addAttr(NewAttr);
+}
+
+/// \brief Check class-level dllimport/dllexport attribute.
+static void checkDLLAttribute(Sema &S, CXXRecordDecl *Class) {
+ Attr *ClassAttr = getDLLAttr(Class);
+ if (!ClassAttr)
+ return;
+
+ bool ClassExported = ClassAttr->getKind() == attr::DLLExport;
+
+ // Force declaration of implicit members so they can inherit the attribute.
+ S.ForceDeclarationOfImplicitMembers(Class);
+
+ // FIXME: All bases must be exportable. MSVC doesn't seem to warn about
+ // this, but we should. We also need to propagate the attribute upwards to
+ // template specialization bases.
+ for (Decl *D : Class->decls()) {
+ if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ checkDLLClassMember(S, VD, ClassAttr,
+ VD->getType()->getAsCXXRecordDecl());
+ }
+
+ if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+ checkDLLClassMember(S, MD, ClassAttr,
+ MD->getReturnType()->getAsCXXRecordDecl());
+
+ if (ClassExported && !MD->isDefaulted() && !MD->isDeleted()) {
+ // Instantiate non-default methods.
+ S.MarkFunctionReferenced(Class->getLocation(), MD);
+ } else if (ClassExported && !MD->isDeleted() &&
+ (!MD->isTrivial() || MD->isCopyAssignmentOperator())) {
+ // Also instantiate non-trivial default methods and the copy assignment
+ // operator.
+ S.MarkFunctionReferenced(Class->getLocation(), MD);
+ // Resolve its exception specification; CodeGen needs it.
+ auto *FPT = MD->getType()->getAs<FunctionProtoType>();
+ S.ResolveExceptionSpec(Class->getLocation(), FPT);
+ S.ActOnFinishInlineMethodDef(MD);
+ }
+ }
+ }
+}
+
/// \brief Perform semantic checks on a class definition that has been
/// completing, introducing implicitly-declared members, checking for
/// abstract types, etc.
@@ -4510,6 +4582,8 @@
// instantiated (e.g. meta-functions). This doesn't apply to classes that
// have inheriting constructors.
DeclareInheritingConstructors(Record);
+
+ checkDLLAttribute(*this, Record);
}
/// Look up the special member function that would be called by a special
Index: test/CodeGenCXX/dllexport.cpp
===================================================================
--- test/CodeGenCXX/dllexport.cpp
+++ test/CodeGenCXX/dllexport.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -emit-llvm < %s | FileCheck %s
+// RUN: %clang_cc1 -triple i686-pc-win32 -fno-rtti -x c++ -emit-llvm < %s | FileCheck %s
+// RUN: %clang_cc1 -triple i686-pc-win32 -O1 -mconstructor-aliases -fno-rtti -x c++ -emit-llvm < %s | FileCheck %s
#define DLLEXPORT __declspec(dllexport)
@@ -23,6 +24,62 @@
};
-void user() {
+struct DLLEXPORT T {
+ // Copy assignment operator:
+ // CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.T* @"\01??4T@@QAEAAU0@ABU0@@Z"
+
+ void a() {}
+ // CHECK-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?a@T@@QAEXXZ"
+
+ static int b;
+ // CHECK-DAG: @"\01?b@T@@2HA" = external dllexport global i32
+
+ static int c;
+ // CHECK-DAG: @"\01?c@T@@2HA" = dllexport global i32 0, align 4
+};
+
+int T::c;
+
+template <typename T> struct DLLEXPORT U { void foo() {} };
+// The U<int> specialization below must cause the following to be emitted:
+// CHECK-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?foo@?$U@H@@QAEXXZ"
+// CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.U* @"\01??4?$U@H@@QAEAAU0@ABU0@@Z"
+struct DLLEXPORT V : public U<int> { };
+
+
+struct DLLEXPORT W { virtual void foo() {} };
+// Default ctor:
+// CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.W* @"\01??0W@@QAE@XZ"
+// Copy ctor:
+// CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.W* @"\01??0W@@QAE@ABU0@@Z"
+// vftable:
+// CHECK-DAG: @"\01??_7W@@6B@" = weak_odr dllexport unnamed_addr constant [1 x i8*] [i8* bitcast (void (%struct.W*)* @"\01?foo@W@@UAEXXZ" to i8*)]
+
+struct DLLEXPORT X : public virtual W {};
+// vbtable:
+// CHECK-DAG: @"\01??_8X@@7B@" = weak_odr dllexport unnamed_addr constant [2 x i32] [i32 0, i32 4]
+
+struct DLLEXPORT Y {
+ int x;
+};
+
+struct DLLEXPORT Z { virtual ~Z() {} };
+// The scalar deleting dtor does not get exported:
+// CHECK-DAG: define weak_odr x86_thiscallcc void @"\01??_GZ@@UAEPAXI@Z"
+
+// The user-defined dtor does get exported:
+// CHECK-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??1Z@@UAE@XZ"
+
+namespace DontUseDtorAlias {
+ struct DLLEXPORT A { ~A(); };
+ struct DLLEXPORT B : A { ~B(); };
+ A::~A() { }
+ B::~B() { }
+ // Emit a real definition of B's constructor; don't alias it to A's.
+ // CHECK-DAG: define dllexport x86_thiscallcc void @"\01??1B@DontUseDtorAlias@@QAE@XZ"
+}
+
+int user() {
a();
+ return T::b;
}
Index: test/CodeGenCXX/dllimport.cpp
===================================================================
--- test/CodeGenCXX/dllimport.cpp
+++ test/CodeGenCXX/dllimport.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -O2 -disable-llvm-optzns -emit-llvm < %s | FileCheck %s
+// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -fno-rtti -O2 -disable-llvm-optzns -emit-llvm < %s | FileCheck %s
+// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -fno-rtti -mconstructor-aliases -O2 -disable-llvm-optzns -emit-llvm < %s | FileCheck %s
#define DLLIMPORT __declspec(dllimport)
@@ -17,9 +18,53 @@
// CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01?a@S@@QAEXXZ"
};
-void user(S* s) {
+struct DLLIMPORT T {
+ void a() {}
+ // CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01?a@T@@QAEXXZ"
+
+ static int b;
+ // CHECK-DAG: @"\01?b@T@@2HA" = external dllimport global i32
+};
+
+template <typename T> struct DLLIMPORT U { void foo() {} };
+// CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01?foo@?$U@H@@QAEXXZ"
+struct DLLIMPORT V : public U<int> { };
+
+struct DLLIMPORT W { virtual void foo() {} };
+// vftable:
+// CHECK-DAG: @"\01??_7W@@6B@" = available_externally dllimport unnamed_addr constant [1 x i8*] [i8* bitcast (void (%struct.W*)* @"\01?foo@W@@UAEXXZ" to i8*)]
+
+struct DLLIMPORT X : public virtual W {};
+// vbtable:
+// CHECK-DAG: @"\01??_8X@@7B@" = available_externally dllimport unnamed_addr constant [2 x i32] [i32 0, i32 4]
+
+struct DLLIMPORT Y {
+ int x;
+};
+
+struct DLLIMPORT Z { virtual ~Z() {} };
+// User-defined dtor:
+// CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01??1Z@@UAE@XZ"
+
+namespace DontUseDtorAlias {
+ struct DLLIMPORT A { ~A(); };
+ struct DLLIMPORT B : A { ~B(); };
+ inline A::~A() { }
+ inline B::~B() { }
+ // Emit a real definition of B's constructor; don't alias it to A's.
+ // CHECK-DAG: available_externally dllimport x86_thiscallcc void @"\01??1B@DontUseDtorAlias@@QAE@XZ"
+}
+
+void user() {
a();
b();
c<int>();
- s->a();
+ (void) &S::a;
+ (void) &T::a;
+ (void) T::b;
+ (void) &V::foo;
+ W w;
+ X x;
+ Z z;
+ DontUseDtorAlias::B b;
}
Index: test/SemaCXX/dllexport.cpp
===================================================================
--- test/SemaCXX/dllexport.cpp
+++ test/SemaCXX/dllexport.cpp
@@ -16,13 +16,13 @@
// Invalid usage.
-__declspec(dllexport) typedef int typedef1; // expected-warning{{'dllexport' attribute only applies to variables and functions}}
-typedef __declspec(dllexport) int typedef2; // expected-warning{{'dllexport' attribute only applies to variables and functions}}
-typedef int __declspec(dllexport) typedef3; // expected-warning{{'dllexport' attribute only applies to variables and functions}}
-typedef __declspec(dllexport) void (*FunTy)(); // expected-warning{{'dllexport' attribute only applies to variables and functions}}
-enum __declspec(dllexport) Enum {}; // expected-warning{{'dllexport' attribute only applies to variables and functions}}
+__declspec(dllexport) typedef int typedef1; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}}
+typedef __declspec(dllexport) int typedef2; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}}
+typedef int __declspec(dllexport) typedef3; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}}
+typedef __declspec(dllexport) void (*FunTy)(); // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}}
+enum __declspec(dllexport) Enum {}; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}}
#if __has_feature(cxx_strong_enums)
- enum class __declspec(dllexport) EnumClass {}; // expected-warning{{'dllexport' attribute only applies to variables and functions}}
+ enum class __declspec(dllexport) EnumClass {}; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}}
#endif
@@ -210,6 +210,14 @@
//===----------------------------------------------------------------------===//
+// Classes
+//===----------------------------------------------------------------------===//
+
+class __declspec(dllexport) ClassDecl;
+
+class __declspec(dllexport) ClassDef { };
+
+//===----------------------------------------------------------------------===//
// Precedence
//===----------------------------------------------------------------------===//
@@ -249,3 +257,5 @@
void __declspec(dllexport) precedenceRedecl2();
void __declspec(dllimport) precedenceRedecl2() {} // expected-warning{{'dllimport' attribute ignored}}
+
+// FIXME: Precedence rules seem to be different with classes.
Index: test/SemaCXX/dllimport.cpp
===================================================================
--- test/SemaCXX/dllimport.cpp
+++ test/SemaCXX/dllimport.cpp
@@ -15,13 +15,13 @@
// Invalid usage.
-__declspec(dllimport) typedef int typedef1; // expected-warning{{'dllimport' attribute only applies to variables and functions}}
-typedef __declspec(dllimport) int typedef2; // expected-warning{{'dllimport' attribute only applies to variables and functions}}
-typedef int __declspec(dllimport) typedef3; // expected-warning{{'dllimport' attribute only applies to variables and functions}}
-typedef __declspec(dllimport) void (*FunTy)(); // expected-warning{{'dllimport' attribute only applies to variables and functions}}
-enum __declspec(dllimport) Enum {}; // expected-warning{{'dllimport' attribute only applies to variables and functions}}
+__declspec(dllimport) typedef int typedef1; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
+typedef __declspec(dllimport) int typedef2; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
+typedef int __declspec(dllimport) typedef3; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
+typedef __declspec(dllimport) void (*FunTy)(); // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
+enum __declspec(dllimport) Enum {}; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
#if __has_feature(cxx_strong_enums)
- enum class __declspec(dllimport) EnumClass {}; // expected-warning{{'dllimport' attribute only applies to variables and functions}}
+ enum class __declspec(dllimport) EnumClass {}; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
#endif
@@ -225,3 +225,11 @@
template<> __declspec(dllimport) void funcTmpl<ExplicitSpec_Imported>();
template<> __declspec(dllimport) void funcTmpl<ExplicitSpec_Def_Imported>() {} // expected-error{{dllimport cannot be applied to non-inline function definition}}
template<> __declspec(dllimport) inline void funcTmpl<ExplicitSpec_InlineDef_Imported>() {}
+
+//===----------------------------------------------------------------------===//
+// Classes
+//===----------------------------------------------------------------------===//
+
+class __declspec(dllimport) ClassDecl;
+
+class __declspec(dllimport) ClassDef { };
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits