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