[PATCH] Windows support for ObjC DLLIMPORT

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 5 08:54:05 PDT 2016


On Mon, Apr 4, 2016 at 3:34 PM, Wes Witt via cfe-commits
<cfe-commits at lists.llvm.org> wrote:
> Please accept this diff as a change to support dllimport of objective c
> interfaces on windows. I’ve included a new test and changes to an existing
> test as well. All clang tests pass on Linux & Windows. This change is
> required for Microsoft’s use of the clang compiler in support of objective c
> on windows.

Thank you for working on this! Some comments below:

> diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
> index c7a797c..dc22b27 100644
> --- a/include/clang/Basic/Attr.td
> +++ b/include/clang/Basic/Attr.td
> @@ -2023,7 +2023,8 @@ def DLLExport : InheritableAttr, TargetSpecificAttr<TargetWindows> {
>
>  def DLLImport : InheritableAttr, TargetSpecificAttr<TargetWindows> {
>    let Spellings = [Declspec<"dllimport">, GCC<"dllimport">];
> -  let Subjects = SubjectList<[Function, Var, CXXRecord]>;
> +  let Subjects = SubjectList<[Function, Var, CXXRecord, ObjCInterface], WarnDiag,
> +                             "ExpectedFunctionVariableOrClass">;

The diagnostic for this won't indicate that it's also okay to play on
an Objective-C interface decl; is that intentional?

>    let Documentation = [Undocumented];

It would be nice to add some documentation to this, instead of leaving
it totally undocumented. The docs can basically point to MSDN on
__declspec(dllimport), but this would be a good place to let users
know about the Objective-C behavior as well.

>  }
>
> diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp
> index ea164aa..9d2b2f9 100644
> --- a/lib/CodeGen/CGBlocks.cpp
> +++ b/lib/CodeGen/CGBlocks.cpp
> @@ -2334,5 +2334,9 @@ llvm::Constant *CodeGenModule::getNSConcreteStackBlock() {
>                                                 Int8PtrTy->getPointerTo(),
>                                                 nullptr);
>    configureBlocksRuntimeObject(*this, NSConcreteStackBlock);
> +  if (getContext().getLangOpts().MSVCCompat) {
> +    auto *GV = cast<llvm::GlobalValue>(NSConcreteStackBlock->stripPointerCasts());
> +    GV->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
> +  }
>    return NSConcreteStackBlock;
>  }
> diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp
> index bbe1b8b..dcb3a7d 100644
> --- a/lib/CodeGen/CGObjCGNU.cpp
> +++ b/lib/CodeGen/CGObjCGNU.cpp
> @@ -472,7 +472,7 @@ protected:
>
>    /// Emits a pointer to the named class
>    virtual llvm::Value *GetClassNamed(CodeGenFunction &CGF,
> -                                     const std::string &Name, bool isWeak);
> +                                     StringRef Name, bool isWeak, bool isDLLImport);
>
>    /// Looks up the method for sending a message to the specified object.  This
>    /// mechanism differs between the GCC and GNU runtimes, so this method must be
> @@ -866,21 +866,24 @@ protected:
>      }
>
>    llvm::Value *GetClassNamed(CodeGenFunction &CGF,
> -                             const std::string &Name, bool isWeak) override {
> +                             StringRef Name, bool isWeak, bool isDLLImport) override {
>      if (isWeak)
> -      return CGObjCGNU::GetClassNamed(CGF, Name, isWeak);
> +      return CGObjCGNU::GetClassNamed(CGF, Name, isWeak, isDLLImport);
>
>      EmitClassRef(Name);
>
> -    std::string SymbolName = "_OBJC_CLASS_" + Name;
> +    std::string SymbolName = "_OBJC_CLASS_" + Name.str();
>
>      llvm::GlobalVariable *ClassSymbol = TheModule.getGlobalVariable(SymbolName);
>
> -    if (!ClassSymbol)
> +    if (!ClassSymbol) {
>        ClassSymbol = new llvm::GlobalVariable(TheModule, LongTy, false,
>                                               llvm::GlobalValue::ExternalLinkage,
>                                               nullptr, SymbolName);
> -
> +      if (isDLLImport)
> +        ClassSymbol->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);
> +    }
> +
>      return ClassSymbol;
>    }
>
> @@ -1054,8 +1057,8 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion,
>  }
>
>  llvm::Value *CGObjCGNU::GetClassNamed(CodeGenFunction &CGF,
> -                                      const std::string &Name,
> -                                      bool isWeak) {
> +                                      StringRef Name,
> +                                      bool isWeak, bool isDLLImport) {
>    llvm::Constant *ClassName = MakeConstantString(Name);
>    // With the incompatible ABI, this will need to be replaced with a direct
>    // reference to the class symbol.  For the compatible nonfragile ABI we are
> @@ -1077,11 +1080,12 @@ llvm::Value *CGObjCGNU::GetClassNamed(CodeGenFunction &CGF,
>  // techniques can modify the name -> class mapping.
>  llvm::Value *CGObjCGNU::GetClass(CodeGenFunction &CGF,
>                                   const ObjCInterfaceDecl *OID) {
> -  return GetClassNamed(CGF, OID->getNameAsString(), OID->isWeakImported());
> +  return GetClassNamed(CGF, OID->getNameAsString(), OID->isWeakImported(),
> +                       OID->hasAttr<DLLImportAttr>());

getNameAsString() returns a std::string, so the change to StringRef in
GetClassNamed will add an extra conversion for no real purpose.
Perhaps that should remain const std::string & instead?

>  }
>
>  llvm::Value *CGObjCGNU::EmitNSAutoreleasePoolClassRef(CodeGenFunction &CGF) {
> -  return GetClassNamed(CGF, "NSAutoreleasePool", false);
> +  return GetClassNamed(CGF, "NSAutoreleasePool", false, CGF.CGM.getTarget().getTriple().isKnownWindowsMSVCEnvironment());
>  }
>
>  llvm::Value *CGObjCGNU::GetSelector(CodeGenFunction &CGF, Selector Sel,
> @@ -2878,7 +2882,10 @@ llvm::Value *CGObjCGNU::EmitIvarOffset(CodeGenFunction &CGF,
>                           const ObjCIvarDecl *Ivar) {
>    if (CGM.getLangOpts().ObjCRuntime.isNonFragile()) {
>      Interface = FindIvarInterface(CGM.getContext(), Interface, Ivar);
> -    if (RuntimeVersion < 10)
> +    // The MSVC linker cannot have a single global defined as LinkOnceAnyLinkage and
> +    // ExternalLinkage, so create a reference to the ivar global and rely on the
> +    // definition being created as part of GenerateClass.
> +    if (RuntimeVersion < 10 || CGF.CGM.getTarget().getTriple().isKnownWindowsMSVCEnvironment())
>        return CGF.Builder.CreateZExtOrBitCast(
>            CGF.Builder.CreateDefaultAlignedLoad(CGF.Builder.CreateAlignedLoad(
>                    ObjCIvarOffsetVariable(Interface, Ivar),
> diff --git a/test/Sema/dllimport.c b/test/Sema/dllimport.c
> index f863499..b2e2d3c 100644
> --- a/test/Sema/dllimport.c
> +++ b/test/Sema/dllimport.c
> @@ -4,12 +4,12 @@
>  // RUN: %clang_cc1 -triple x86_64-mingw32 -fsyntax-only -fms-extensions -verify -std=c99 -DGNU %s
>
>  // 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 { EnumVal }; // expected-warning{{'dllimport' attribute only applies to variables and functions}}
> -struct __declspec(dllimport) Record {}; // 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 { EnumVal }; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
> +struct __declspec(dllimport) Record {}; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}}
>
>
>
> diff --git a/test/SemaObjC/dllimport.m b/test/SemaObjC/dllimport.m
> new file mode 100644
> index 0000000..b8fde7c
> --- /dev/null
> +++ b/test/SemaObjC/dllimport.m
> @@ -0,0 +1,19 @@
> +// RUN: %clang -fsyntax-only -fms-extensions %s -Xclang -verify
> +#if !__LP64__
> +// expected-no-diagnostics
> +#endif
> +
> + at interface NSObject @end
> +
> +#if __LP64__
> +__declspec(dllimport) // expected-warning {{__declspec attribute 'dllimport' is not supported}}
> +#else
> +__declspec(dllimport)
> +#endif
> + at interface testinterface : NSObject
> +- (void)testmethod;
> + at end
> +
> + at implementation testinterface
> +- (void)testmethod { }
> + at end
> \ No newline at end of file

Can you add a newline to the end of this file?

~Aaron


More information about the cfe-commits mailing list