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 cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits