mstorsjo created this revision.
mstorsjo added reviewers: rnk, majnemer, smeenai, compnerd.
The first member of the type info structs/objects is a pointer to the vtable of
the type info class. If the standard C++ library that provides this vtable is
linked as a DLL, this field of the struct needs to be initialized differently.
If statically initializing a variable with a pointer to a dllimported variable,
that initalization can't be done as normal static initialization, since the
address of the variable only will be available at runtime via the IAT.
For a struct/class with dllimported members, clang skips the normal static
initalization and instead produces a constructor that will do the equivalent
initialization at runtime.
For type info objects that are instantiated in ItaniumCXXABI, it's not enough
to just set the dllimport attribute on the vtable pointer to invoke the
existing generation of a constructor in
CodeGenModule::EmitCXXGlobalVarDeclInitFunc in CGDeclCXX.cpp. Instead
ItaniumCXXABI needs to manually produce the equivalent code for the runtime
initialization as well, without a VarDecl for this struct.
To enable this behaviour, a new compiler flag, -fcxx-dll, is added, that can be
set when building code that expects to be linking to the standard C++ library
as a DLL.
This hasn't been an issue before, if linking with GNU ld, since GNU ld
automatically can handle references to variables that weren't marked as
dllimport during compilation, if the undefined references are found in a DLL
import library. Since lld doesn't support this behaviour, we need to properly
use dllimport mechanisms even for this field.
The actual implementation isn't very elegant yet (it's only a proof of concept
so far) - directions on how to do it better are welcome.
Repository:
rC Clang
https://reviews.llvm.org/D43184
Files:
docs/ClangCommandLineReference.rst
include/clang/Driver/Options.td
include/clang/Frontend/CodeGenOptions.def
lib/CodeGen/CodeGenModule.h
lib/CodeGen/ItaniumCXXABI.cpp
lib/Driver/ToolChains/Clang.cpp
lib/Frontend/CompilerInvocation.cpp
test/CodeGenCXX/rtti-mingw64.cpp
Index: test/CodeGenCXX/rtti-mingw64.cpp
===================================================================
--- test/CodeGenCXX/rtti-mingw64.cpp
+++ test/CodeGenCXX/rtti-mingw64.cpp
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-windows-gnu %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -fcxx-dll -fno-cxx-dll -triple x86_64-windows-gnu %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -fcxx-dll -triple x86_64-windows-gnu %s -emit-llvm -o - | FileCheck %s -check-prefix=CHECK-DLL
struct A { int a; };
struct B : virtual A { int b; };
B b;
@@ -16,3 +18,23 @@
// CHECK-SAME: i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*),
// This i64 is important, it should be an i64, not an i32.
// CHECK-SAME: i64 -6141 }, comdat
+
+
+// CHECK-DLL: @_ZTVN10__cxxabiv117__class_type_infoE = external dllimport global i8*
+
+// CHECK-DLL: @_ZTI1C = linkonce_odr global { i8*, i8* }
+// The first field of the typeinfo, the vtable pointer, is initialized to null
+// CHECK-DLL-SAME: i8* null,
+// CHECK-DLL-SAME: i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1C, i32 0, i32 0) }, comdat
+// CHECK-DLL: @_ZTI1B = linkonce_odr global { i8*, i8*, i32, i32, i8*, i64 }
+// CHECK-DLL-SAME: i8* null,
+// CHECK-DLL-SAME: i8* getelementptr inbounds ([3 x i8], [3 x i8]* @_ZTS1B, i32 0, i32 0),
+
+// CHECK-DLL: @llvm.global_ctors = appending global
+// Check for high priority constructors (normal constructors run at priority 65535)
+// CHECK-DLL-SAME: { i32 0, void ()* @__cxx_global_var_init.1, i8* null },
+
+// CHECK-DLL: define internal void @__cxx_global_var_init.1()
+// Check that the runtime constructor initializes the vtable pointer in the typeinfo.
+// CHECK-DLL: store i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i64 2) to i8*),
+// CHECK-DLL-SAME: i8** getelementptr inbounds ({ i8*, i8* }, { i8*, i8* }* @_ZTI1C, i32 0, i32 0)
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -632,6 +632,7 @@
Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions);
Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit);
Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases);
+ Opts.CXXDll = Args.hasFlag(OPT_fcxx_dll, OPT_fno_cxx_dll, false);
Opts.CodeModel = getCodeModel(Args, Diags);
Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass);
Opts.DisableFPElim =
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -4276,6 +4276,10 @@
}
}
+ // Link to the C++ standard library as a DLL.
+ if (Args.hasFlag(options::OPT_fcxx_dll, options::OPT_fno_cxx_dll, false))
+ CmdArgs.push_back("-fcxx-dll");
+
// C++ "sane" operator new.
if (!Args.hasFlag(options::OPT_fassume_sane_operator_new,
options::OPT_fno_assume_sane_operator_new))
Index: lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- lib/CodeGen/ItaniumCXXABI.cpp
+++ lib/CodeGen/ItaniumCXXABI.cpp
@@ -2958,6 +2958,9 @@
llvm::Constant *VTable =
CGM.getModule().getOrInsertGlobal(VTableName, CGM.Int8PtrTy);
+ llvm::GlobalValue *GV = dyn_cast<llvm::GlobalValue>(VTable);
+ if (GV && CGM.getCodeGenOpts().CXXDll)
+ GV->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
llvm::Type *PtrDiffTy =
CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType());
@@ -3058,6 +3061,11 @@
// Add the vtable pointer.
BuildVTablePointer(cast<Type>(Ty));
+ bool DLLImport = CGM.getCodeGenOpts().CXXDll;
+ llvm::Constant *VTable = Fields.back();
+ if (DLLImport)
+ Fields.back() = llvm::Constant::getNullValue(CGM.Int8PtrTy);
+
// And the name.
llvm::GlobalVariable *TypeName = GetAddrOfTypeName(Ty, Linkage);
llvm::Constant *TypeNameField;
@@ -3171,7 +3179,7 @@
llvm::Module &M = CGM.getModule();
llvm::GlobalVariable *GV =
new llvm::GlobalVariable(M, Init->getType(),
- /*Constant=*/true, Linkage, Init, Name);
+ /*Constant=*/ !DLLImport, Linkage, Init, Name);
// If there's already an old global variable, replace it with the new one.
if (OldGV) {
@@ -3185,6 +3193,31 @@
if (CGM.supportsCOMDAT() && GV->isWeakForLinker())
GV->setComdat(M.getOrInsertComdat(GV->getName()));
+ if (DLLImport) {
+ llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, false);
+ SmallString<256> FnName;
+ {
+ llvm::raw_svector_ostream Out(FnName);
+ CGM.getCXXABI().getMangleContext().mangleDynamicInitializer(
+ NULL /* VarDecl */, Out);
+ }
+ llvm::Function *Fn = CGM.CreateGlobalInitOrDestructFunction(
+ FTy, FnName.str(), CGM.getTypes().arrangeNullaryFunction());
+
+ CodeGenFunction CGF(CGM);
+ CGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, Fn,
+ CGM.getTypes().arrangeNullaryFunction(), FunctionArgList());
+ llvm::Constant *Zero = llvm::Constant::getNullValue(CGM.Int32Ty);
+ llvm::Constant *Zeros[] = {Zero, Zero};
+ llvm::Value *FieldPtr = llvm::ConstantExpr::getInBoundsGetElementPtr(
+ GV->getValueType(), GV, Zeros);
+ CGF.Builder.CreateDefaultAlignedStore(
+ llvm::ConstantExpr::getBitCast(VTable, CGM.Int8PtrTy), FieldPtr);
+ CGF.FinishFunction();
+ // Run with priority 0, before any user defined ctors
+ CGM.AddGlobalCtor(Fn, 0);
+ }
+
// The Itanium ABI specifies that type_info objects must be globally
// unique, with one exception: if the type is an incomplete class
// type or a (possibly indirect) pointer to one. That exception
Index: lib/CodeGen/CodeGenModule.h
===================================================================
--- lib/CodeGen/CodeGenModule.h
+++ lib/CodeGen/CodeGenModule.h
@@ -1308,8 +1308,10 @@
llvm::Function *InitFunc, InitSegAttr *ISA);
// FIXME: Hardcoding priority here is gross.
+public:
void AddGlobalCtor(llvm::Function *Ctor, int Priority = 65535,
llvm::Constant *AssociatedData = nullptr);
+private:
void AddGlobalDtor(llvm::Function *Dtor, int Priority = 65535);
/// EmitCtorList - Generates a global array of functions and priorities using
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -45,6 +45,7 @@
CODEGENOPT(CXAAtExit , 1, 1) ///< Use __cxa_atexit for calling destructors.
CODEGENOPT(CXXCtorDtorAliases, 1, 0) ///< Emit complete ctors/dtors as linker
///< aliases to base ctors when possible.
+CODEGENOPT(CXXDll , 1, 0) ///< Set when -fcxx-dll is enabled.
CODEGENOPT(DataSections , 1, 0) ///< Set when -fdata-sections is enabled.
CODEGENOPT(UniqueSectionNames, 1, 1) ///< Set for -funique-section-names.
CODEGENOPT(DisableFPElim , 1, 0) ///< Set when -fomit-frame-pointer is enabled.
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -760,6 +760,10 @@
def fno_crash_diagnostics : Flag<["-"], "fno-crash-diagnostics">, Group<f_clang_Group>, Flags<[NoArgumentUnused]>,
HelpText<"Disable auto-generation of preprocessed source files and a script for reproduction during a clang crash">;
def fcreate_profile : Flag<["-"], "fcreate-profile">, Group<f_Group>;
+def fcxx_dll: Flag<["-"], "fcxx-dll">, Group<f_Group>,
+ HelpText<"Generate code for a C++ stdlib linked as a DLL">, Flags<[CC1Option]>;
+def fno_cxx_dll: Flag<["-"], "fno-cxx-dll">, Group<f_Group>,
+ HelpText<"Don't generate code for a C++ stdlib linked as a DLL">, Flags<[CC1Option]>;
def fcxx_exceptions: Flag<["-"], "fcxx-exceptions">, Group<f_Group>,
HelpText<"Enable C++ exceptions">, Flags<[CC1Option]>;
def fcxx_modules : Flag <["-"], "fcxx-modules">, Group<f_Group>,
Index: docs/ClangCommandLineReference.rst
===================================================================
--- docs/ClangCommandLineReference.rst
+++ docs/ClangCommandLineReference.rst
@@ -1217,6 +1217,8 @@
.. option:: -fcreate-profile
+.. option:: -fcxx-dll, -fno-cxx-dll
+
.. option:: -fcxx-exceptions, -fno-cxx-exceptions
Enable C++ exceptions
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits