From f24bd3bdba33297223f2dcbc3bcfae8b844a69e2 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Fri, 26 Jul 2013 00:53:29 +0000 Subject: [PATCH] Fix GNU ObjC ABI for a message returning a struct. This allows the ObjFW runtime to correctly implement message forwarding for messages which return a struct. Patch by Jonathan Schleifer. llvm-svn: 187174 --- clang/docs/LanguageExtensions.rst | 17 ++++++++++++ clang/lib/CodeGen/CGObjCGNU.cpp | 51 ++++++++++++++++++++++++++--------- clang/lib/Lex/PPMacroExpansion.cpp | 1 + clang/test/CodeGenObjC/stret_lookup.m | 29 ++++++++++++++++++++ 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 clang/test/CodeGenObjC/stret_lookup.m diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 1917e2b..e8826b5 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1255,6 +1255,23 @@ Further examples of these attributes are available in the static analyzer's `lis Query for these features with ``__has_attribute(ns_consumed)``, ``__has_attribute(ns_returns_retained)``, etc. +objc_msg_lookup_stret +--------------------- + +Traditionally, if a runtime is used that follows the GNU Objective-C ABI, a +call to objc_msg_lookup() would be emitted for each message send, which would +return a pointer to the actual implementation of the method. However, +objc_msg_lookup() has no information at all about the method signature of the +actual method. Therefore, certain features like forwarding messages cannot be +correctly implemented for methods returning structs using objc_msg_lookup(), as +methods returning structs use a slightly different calling convention. + +To work around this, Clang emits calls to objc_msg_lookup_stret() instead for +methods that return structs if the runtime supports this, allowing the runtime +to use a different forwarding handler for methods returning structs. + +To check if Clang emits calls to objc_msg_lookup_stret(), +__has_feature(objc_msg_lookup_stret) can be used. Function Overloading in C ========================= diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 2fd379d..dc80a75 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -454,13 +454,15 @@ protected: virtual llvm::Value *LookupIMP(CodeGenFunction &CGF, llvm::Value *&Receiver, llvm::Value *cmd, - llvm::MDNode *node) = 0; + llvm::MDNode *node, + MessageSendInfo &MSI) = 0; /// Looks up the method for sending a message to a superclass. This /// mechanism differs between the GCC and GNU runtimes, so this method must /// be overridden in subclasses. virtual llvm::Value *LookupIMPSuper(CodeGenFunction &CGF, llvm::Value *ObjCSuper, - llvm::Value *cmd) = 0; + llvm::Value *cmd, + MessageSendInfo &MSI) = 0; /// Libobjc2 uses a bitfield representation where small(ish) bitfields are /// stored in a 64-bit value with the low bit set to 1 and the remaining 63 /// bits set to their values, LSB first, while larger ones are stored in a @@ -596,7 +598,8 @@ protected: virtual llvm::Value *LookupIMP(CodeGenFunction &CGF, llvm::Value *&Receiver, llvm::Value *cmd, - llvm::MDNode *node) { + llvm::MDNode *node, + MessageSendInfo &MSI) { CGBuilderTy &Builder = CGF.Builder; llvm::Value *args[] = { EnforceType(Builder, Receiver, IdTy), @@ -607,7 +610,8 @@ protected: } virtual llvm::Value *LookupIMPSuper(CodeGenFunction &CGF, llvm::Value *ObjCSuper, - llvm::Value *cmd) { + llvm::Value *cmd, + MessageSendInfo &MSI) { CGBuilderTy &Builder = CGF.Builder; llvm::Value *lookupArgs[] = {EnforceType(Builder, ObjCSuper, PtrToObjCSuperTy), cmd}; @@ -655,7 +659,8 @@ class CGObjCGNUstep : public CGObjCGNU { virtual llvm::Value *LookupIMP(CodeGenFunction &CGF, llvm::Value *&Receiver, llvm::Value *cmd, - llvm::MDNode *node) { + llvm::MDNode *node, + MessageSendInfo &MSI) { CGBuilderTy &Builder = CGF.Builder; llvm::Function *LookupFn = SlotLookupFn; @@ -693,7 +698,8 @@ class CGObjCGNUstep : public CGObjCGNU { } virtual llvm::Value *LookupIMPSuper(CodeGenFunction &CGF, llvm::Value *ObjCSuper, - llvm::Value *cmd) { + llvm::Value *cmd, + MessageSendInfo &MSI) { CGBuilderTy &Builder = CGF.Builder; llvm::Value *lookupArgs[] = {ObjCSuper, cmd}; @@ -797,31 +803,46 @@ protected: /// The GCC ABI message lookup function. Returns an IMP pointing to the /// method implementation for this message. LazyRuntimeFunction MsgLookupFn; + /// stret lookup function. While this does not seem to make sense at the + /// first look, this is required to call the correct forwarding function. + LazyRuntimeFunction MsgLookupFnSRet; /// The GCC ABI superclass message lookup function. Takes a pointer to a /// structure describing the receiver and the class, and a selector as /// arguments. Returns the IMP for the corresponding method. - LazyRuntimeFunction MsgLookupSuperFn; + LazyRuntimeFunction MsgLookupSuperFn, MsgLookupSuperFnSRet; virtual llvm::Value *LookupIMP(CodeGenFunction &CGF, llvm::Value *&Receiver, llvm::Value *cmd, - llvm::MDNode *node) { + llvm::MDNode *node, + MessageSendInfo &MSI) { CGBuilderTy &Builder = CGF.Builder; llvm::Value *args[] = { EnforceType(Builder, Receiver, IdTy), EnforceType(Builder, cmd, SelectorTy) }; - llvm::CallSite imp = CGF.EmitRuntimeCallOrInvoke(MsgLookupFn, args); + + llvm::CallSite imp; + if (CGM.ReturnTypeUsesSRet(MSI.CallInfo)) + imp = CGF.EmitRuntimeCallOrInvoke(MsgLookupFnSRet, args); + else + imp = CGF.EmitRuntimeCallOrInvoke(MsgLookupFn, args); + imp->setMetadata(msgSendMDKind, node); return imp.getInstruction(); } virtual llvm::Value *LookupIMPSuper(CodeGenFunction &CGF, llvm::Value *ObjCSuper, - llvm::Value *cmd) { + llvm::Value *cmd, + MessageSendInfo &MSI) { CGBuilderTy &Builder = CGF.Builder; llvm::Value *lookupArgs[] = {EnforceType(Builder, ObjCSuper, PtrToObjCSuperTy), cmd}; - return CGF.EmitNounwindRuntimeCall(MsgLookupSuperFn, lookupArgs); + + if (CGM.ReturnTypeUsesSRet(MSI.CallInfo)) + return CGF.EmitNounwindRuntimeCall(MsgLookupSuperFnSRet, lookupArgs); + else + return CGF.EmitNounwindRuntimeCall(MsgLookupSuperFn, lookupArgs); } virtual llvm::Value *GetClassNamed(CodeGenFunction &CGF, @@ -847,9 +868,13 @@ public: CGObjCObjFW(CodeGenModule &Mod): CGObjCGNU(Mod, 9, 3) { // IMP objc_msg_lookup(id, SEL); MsgLookupFn.init(&CGM, "objc_msg_lookup", IMPTy, IdTy, SelectorTy, NULL); + MsgLookupFnSRet.init(&CGM, "objc_msg_lookup_stret", IMPTy, IdTy, + SelectorTy, NULL); // IMP objc_msg_lookup_super(struct objc_super*, SEL); MsgLookupSuperFn.init(&CGM, "objc_msg_lookup_super", IMPTy, PtrToObjCSuperTy, SelectorTy, NULL); + MsgLookupSuperFnSRet.init(&CGM, "objc_msg_lookup_super_stret", IMPTy, + PtrToObjCSuperTy, SelectorTy, NULL); } }; } // end anonymous namespace @@ -1291,7 +1316,7 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, ObjCSuper = EnforceType(Builder, ObjCSuper, PtrToObjCSuperTy); // Get the IMP - llvm::Value *imp = LookupIMPSuper(CGF, ObjCSuper, cmd); + llvm::Value *imp = LookupIMPSuper(CGF, ObjCSuper, cmd, MSI); imp = EnforceType(Builder, imp, MSI.MessengerType); llvm::Value *impMD[] = { @@ -1390,7 +1415,7 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF, // given platform), so we switch (CGM.getCodeGenOpts().getObjCDispatchMethod()) { case CodeGenOptions::Legacy: - imp = LookupIMP(CGF, Receiver, cmd, node); + imp = LookupIMP(CGF, Receiver, cmd, node, MSI); break; case CodeGenOptions::Mixed: case CodeGenOptions::NonLegacy: diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 2a93239..0a7f079 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -918,6 +918,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("objc_nonfragile_abi", LangOpts.ObjCRuntime.isNonFragile()) .Case("objc_property_explicit_atomic", true) // Does clang support explicit "atomic" keyword? .Case("objc_weak_class", LangOpts.ObjCRuntime.hasWeakClassImport()) + .Case("objc_msg_lookup_stret", LangOpts.ObjCRuntime.getKind() == ObjCRuntime::ObjFW) .Case("ownership_holds", true) .Case("ownership_returns", true) .Case("ownership_takes", true) diff --git a/clang/test/CodeGenObjC/stret_lookup.m b/clang/test/CodeGenObjC/stret_lookup.m new file mode 100644 index 0000000..c9ac713 --- /dev/null +++ b/clang/test/CodeGenObjC/stret_lookup.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -DSTRET -triple x86_64-pc-linux-gnu -fobjc-runtime=objfw -emit-llvm -o - %s | FileCheck -check-prefix=HASSTRET %s +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fobjc-runtime=gcc -emit-llvm -o - %s | FileCheck -check-prefix=NOSTRET %s + +// Test stret lookup + +struct test { + char test[1024]; +}; +@interface Test0 ++ (struct test)test; +@end +void test0(void) { + struct test t; +#if (defined(STRET) && __has_feature(objc_msg_lookup_stret)) || \ + (!defined(STRET) && !__has_feature(objc_msg_lookup_stret)) + t = [Test0 test]; +#endif + (void)t; +} + +// HASSTRET: define void @test0() +// HASSTRET: [[T0:%.*]] = call i8* (i8*, i8*, ...)* (i8*, i8*)* @objc_msg_lookup_stret(i8* bitcast (i64* @_OBJC_CLASS_Test0 to i8*), +// HASSTRET-NEXT: [[T1:%.*]] = bitcast i8* (i8*, i8*, ...)* [[T0]] to void (%struct.test*, i8*, i8*)* +// HASSTRET-NEXT: call void [[T1]](%struct.test* sret %tmp, i8* bitcast (i64* @_OBJC_CLASS_Test0 to i8*), + +// NOSTRET: define void @test0() +// NOSTRET: [[T0:%.*]] = call i8* (i8*, i8*, ...)* (i8*, i8*)* @objc_msg_lookup(i8* %0, +// NOSTRET-NEXT: [[T1:%.*]] = bitcast i8* (i8*, i8*, ...)* [[T0]] to void (%struct.test*, i8*, i8*)* +// NOSTRET-NEXT: call void [[T1]](%struct.test* sret %tmp, i8* %0, i8* bitcast ([2 x { i8*, i8* }]* -- 2.7.4