From 6d73f268995a01b224502fde091305b0c41d75e9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 10 Sep 2015 22:27:50 +0000 Subject: [PATCH] Support noreturn in limited contexts on Objective-C message sends. rdar://6198039 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247350 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 71 +++++++++++++++++++---- test/CodeGenObjC/attr-noreturn.m | 99 ++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 test/CodeGenObjC/attr-noreturn.m diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index d4341b3385..8c62eb9dde 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -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(CodeGen::CodeGenFunction &CGF, 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::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, 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::CodeGenFunction &CGF, 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::CodeGenFunction &CGF, 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(CGF.CurCodeDecl)) { + auto Self = CurMethod->getSelfDecl(); + if (Self->getType().isConstQualified()) { + if (auto LI = dyn_cast(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::CodeGenFunction &CGF, 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()) { 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() && !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::GenerateMessageSend(CodeGen::CodeGenFunction &CGF, : 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::GenerateMessageSendSuper(CodeGen::CodeGenFunction &CGF, : 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, diff --git a/test/CodeGenObjC/attr-noreturn.m b/test/CodeGenObjC/attr-noreturn.m new file mode 100644 index 0000000000..c413d0557a --- /dev/null +++ b/test/CodeGenObjC/attr-noreturn.m @@ -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)) +@interface Root +- (instancetype) init; +@end + +@interface Base : Root +@end + +@interface Middle : Base ++ (void) abort __attribute__((noreturn)); +- (void) fail __attribute__((noreturn)); +@end + +@interface Derived : Middle +@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)) +@interface WeakMiddle : Base +@end + +@interface WeakDerived : WeakMiddle ++ (void) abort __attribute__((noreturn)); +@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* {{.*}}){{$}} + +@interface Derived (MyMethods) +@end + +@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]] +@end + +// CHECK: attributes [[NORETURN]] = { noreturn } + \ No newline at end of file -- 2.40.0