r247350 - Support noreturn in limited contexts on Objective-C message sends.
John McCall via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 10 15:27:51 PDT 2015
Author: rjmccall
Date: Thu Sep 10 17:27:50 2015
New Revision: 247350
URL: http://llvm.org/viewvc/llvm-project?rev=247350&view=rev
Log:
Support noreturn in limited contexts on Objective-C message sends.
rdar://6198039
Added:
cfe/trunk/test/CodeGenObjC/attr-noreturn.m
Modified:
cfe/trunk/lib/CodeGen/CGObjCMac.cpp
Modified: cfe/trunk/lib/CodeGen/CGObjCMac.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjCMac.cpp?rev=247350&r1=247349&r2=247350&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjCMac.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjCMac.cpp Thu Sep 10 17:27:50 2015
@@ -1029,6 +1029,7 @@ protected:
bool IsSuper,
const CallArgList &CallArgs,
const ObjCMethodDecl *OMD,
+ const ObjCInterfaceDecl *ClassReceiver,
const ObjCCommonTypesHelper &ObjCTypes);
/// EmitImageInfo - Emit the image info marker used to encode some module
@@ -1833,7 +1834,7 @@ CGObjCMac::GenerateMessageSendSuper(Code
return EmitMessageSend(CGF, Return, ResultType,
EmitSelector(CGF, Sel),
ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
- true, CallArgs, Method, ObjCTypes);
+ true, CallArgs, Method, Class, ObjCTypes);
}
/// Generate code for a message send expression.
@@ -1848,7 +1849,16 @@ CodeGen::RValue CGObjCMac::GenerateMessa
return EmitMessageSend(CGF, Return, ResultType,
EmitSelector(CGF, Sel),
Receiver, CGF.getContext().getObjCIdType(),
- false, CallArgs, Method, ObjCTypes);
+ false, CallArgs, Method, Class, ObjCTypes);
+}
+
+static bool isWeakLinkedClass(const ObjCInterfaceDecl *ID) {
+ do {
+ if (ID->isWeakImported())
+ return true;
+ } while ((ID = ID->getSuperClass()));
+
+ return false;
}
CodeGen::RValue
@@ -1861,6 +1871,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen
bool IsSuper,
const CallArgList &CallArgs,
const ObjCMethodDecl *Method,
+ const ObjCInterfaceDecl *ClassReceiver,
const ObjCCommonTypesHelper &ObjCTypes) {
CallArgList ActualArgs;
if (!IsSuper)
@@ -1877,11 +1888,38 @@ CGObjCCommonMac::EmitMessageSend(CodeGen
CGM.getContext().getCanonicalType(ResultType) &&
"Result type mismatch!");
+ bool ReceiverCanBeNull = true;
+
+ // Super dispatch assumes that self is non-null; even the messenger
+ // doesn't have a null check internally.
+ if (IsSuper) {
+ ReceiverCanBeNull = false;
+
+ // If this is a direct dispatch of a class method, check whether the class,
+ // or anything in its hierarchy, was weak-linked.
+ } else if (ClassReceiver && Method && Method->isClassMethod()) {
+ ReceiverCanBeNull = isWeakLinkedClass(ClassReceiver);
+
+ // If we're emitting a method, and self is const (meaning just ARC, for now),
+ // and the receiver is a load of self, then self is a valid object.
+ } else if (auto CurMethod =
+ dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {
+ auto Self = CurMethod->getSelfDecl();
+ if (Self->getType().isConstQualified()) {
+ if (auto LI = dyn_cast<llvm::LoadInst>(Arg0->stripPointerCasts())) {
+ llvm::Value *SelfAddr = CGF.GetAddrOfLocalVar(Self).getPointer();
+ if (SelfAddr == LI->getPointerOperand()) {
+ ReceiverCanBeNull = false;
+ }
+ }
+ }
+ }
+
NullReturnState nullReturn;
llvm::Constant *Fn = nullptr;
if (CGM.ReturnSlotInterferesWithArgs(MSI.CallInfo)) {
- if (!IsSuper) nullReturn.init(CGF, Arg0);
+ if (ReceiverCanBeNull) nullReturn.init(CGF, Arg0);
Fn = (ObjCABI == 2) ? ObjCTypes.getSendStretFn2(IsSuper)
: ObjCTypes.getSendStretFn(IsSuper);
} else if (CGM.ReturnTypeUsesFPRet(ResultType)) {
@@ -1898,22 +1936,33 @@ CGObjCCommonMac::EmitMessageSend(CodeGen
Fn = (ObjCABI == 2) ? ObjCTypes.getSendFn2(IsSuper)
: ObjCTypes.getSendFn(IsSuper);
}
-
- bool requiresnullCheck = false;
- if (CGM.getLangOpts().ObjCAutoRefCount && Method)
+
+ // Emit a null-check if there's a consumed argument other than the receiver.
+ bool RequiresNullCheck = false;
+ if (ReceiverCanBeNull && CGM.getLangOpts().ObjCAutoRefCount && Method) {
for (const auto *ParamDecl : Method->params()) {
if (ParamDecl->hasAttr<NSConsumedAttr>()) {
if (!nullReturn.NullBB)
nullReturn.init(CGF, Arg0);
- requiresnullCheck = true;
+ RequiresNullCheck = true;
break;
}
}
+ }
+ llvm::Instruction *CallSite;
Fn = llvm::ConstantExpr::getBitCast(Fn, MSI.MessengerType);
- RValue rvalue = CGF.EmitCall(MSI.CallInfo, Fn, Return, ActualArgs);
+ RValue rvalue = CGF.EmitCall(MSI.CallInfo, Fn, Return, ActualArgs,
+ nullptr, &CallSite);
+
+ // Mark the call as noreturn if the method is marked noreturn and the
+ // receiver cannot be null.
+ if (Method && Method->hasAttr<NoReturnAttr>() && !ReceiverCanBeNull) {
+ llvm::CallSite(CallSite).setDoesNotReturn();
+ }
+
return nullReturn.complete(CGF, rvalue, ResultType, CallArgs,
- requiresnullCheck ? Method : nullptr);
+ RequiresNullCheck ? Method : nullptr);
}
static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) {
@@ -6620,7 +6669,7 @@ CGObjCNonFragileABIMac::GenerateMessageS
: EmitMessageSend(CGF, Return, ResultType,
EmitSelector(CGF, Sel),
Receiver, CGF.getContext().getObjCIdType(),
- false, CallArgs, Method, ObjCTypes);
+ false, CallArgs, Method, Class, ObjCTypes);
}
llvm::GlobalVariable *
@@ -6783,7 +6832,7 @@ CGObjCNonFragileABIMac::GenerateMessageS
: EmitMessageSend(CGF, Return, ResultType,
EmitSelector(CGF, Sel),
ObjCSuper.getPointer(), ObjCTypes.SuperPtrCTy,
- true, CallArgs, Method, ObjCTypes);
+ true, CallArgs, Method, Class, ObjCTypes);
}
llvm::Value *CGObjCNonFragileABIMac::EmitSelector(CodeGenFunction &CGF,
Added: cfe/trunk/test/CodeGenObjC/attr-noreturn.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/attr-noreturn.m?rev=247350&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenObjC/attr-noreturn.m (added)
+++ cfe/trunk/test/CodeGenObjC/attr-noreturn.m Thu Sep 10 17:27:50 2015
@@ -0,0 +1,99 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-MRC
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-ARC
+
+__attribute__((objc_root_class))
+ at interface Root
+- (instancetype) init;
+ at end
+
+ at interface Base : Root
+ at end
+
+ at interface Middle : Base
++ (void) abort __attribute__((noreturn));
+- (void) fail __attribute__((noreturn));
+ at end
+
+ at interface Derived : Middle
+ at end
+
+// An arbitrary instance pointer may be null.
+void testInstanceMethod(Derived *x) {
+ [x fail];
+}
+// CHECK-LABEL: @testInstanceMethod
+// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}){{$}}
+
+// A direct call of a class method will normally never have a null receiver.
+void testClassMethod() {
+ [Derived abort];
+}
+// CHECK-LABEL: @testClassMethod
+// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}) [[NORETURN:#[0-9]+]]
+
+__attribute__((weak_import))
+ at interface WeakMiddle : Base
+ at end
+
+ at interface WeakDerived : WeakMiddle
++ (void) abort __attribute__((noreturn));
+ at end
+
+// The class pointer of a weakly-imported class may be null.
+void testWeakImport() {
+ [WeakDerived abort];
+}
+// CHECK-LABEL: @testWeakImport
+// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}){{$}}
+
+ at interface Derived (MyMethods)
+ at end
+
+ at implementation Derived (MyMethods)
+
+// In general, self can be reassigned, so we can't make stronger assumptions.
+// But ARC makes self const in an ordinary method.
+// TODO: do the analysis to take advantage of the dominant case where
+// self is not reassigned.
+- (void) testSelfInstanceMethod {
+ [self fail];
+}
+// CHECK-LABEL: [Derived(MyMethods) testSelfInstanceMethod]
+// CHECK-MRC: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}){{$}}
+// CHECK-ARC: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}) [[NORETURN]]
+
+// The ARC rule doesn't apply in -init methods.
+- (id) initWhileTestingSelfInstanceMethod {
+ self = [super init];
+ [self fail];
+ return self;
+}
+// CHECK-LABEL: [Derived(MyMethods) initWhileTestingSelfInstanceMethod]
+// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}){{$}}
+
+// Same thing applies to class methods.
++ (void) testSelfClassMethod {
+ [self abort];
+}
+// CHECK-LABEL: [Derived(MyMethods) testSelfClassMethod]
+// CHECK-MRC: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}){{$}}
+// CHECK-ARC: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* {{.*}}, i8* {{.*}}) [[NORETURN]]
+
+// Super invocations may never be used with a null pointer; this is a
+// constraint on user code when it isn't enforced by the ARC const-self
+// rule.
+- (void) testSuperInstanceMethod {
+ [super fail];
+}
+// CHECK-LABEL: [Derived(MyMethods) testSuperInstanceMethod]
+// CHECK: call void bitcast (i8* ([[SUPER_T:%.*]]*, i8*, ...)* @objc_msgSendSuper2 to void ([[SUPER_T]]*, i8*)*)([[SUPER_T]]* {{.*}}, i8* {{.*}}) [[NORETURN]]
+
++ (void) testSuperClassMethod {
+ [super abort];
+}
+// CHECK-LABEL: [Derived(MyMethods) testSuperClassMethod]
+// CHECK: call void bitcast (i8* ([[SUPER_T]]*, i8*, ...)* @objc_msgSendSuper2 to void ([[SUPER_T]]*, i8*)*)([[SUPER_T]]* {{.*}}, i8* {{.*}}) [[NORETURN]]
+ at end
+
+// CHECK: attributes [[NORETURN]] = { noreturn }
+
\ No newline at end of file
More information about the cfe-commits
mailing list