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
=========================
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
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),
}
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};
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;
}
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};
/// 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,
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
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[] = {
// 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:
--- /dev/null
+// 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* }]*