mirror of
https://github.com/intel/llvm.git
synced 2026-01-30 22:53:05 +08:00
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
This commit is contained in:
@@ -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
|
||||
=========================
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
29
clang/test/CodeGenObjC/stret_lookup.m
Normal file
29
clang/test/CodeGenObjC/stret_lookup.m
Normal file
@@ -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* }]*
|
||||
Reference in New Issue
Block a user