r174946 - Properly assemble PHIs after a null-checked invoke of objc_msgSend.

John McCall rjmccall at apple.com
Mon Feb 11 21:53:35 PST 2013


Author: rjmccall
Date: Mon Feb 11 23:53:35 2013
New Revision: 174946

URL: http://llvm.org/viewvc/llvm-project?rev=174946&view=rev
Log:
Properly assemble PHIs after a null-checked invoke of objc_msgSend.
rdar://12046763

Modified:
    cfe/trunk/lib/CodeGen/CGObjCMac.cpp
    cfe/trunk/test/CodeGenObjC/complex-double-abi.m
    cfe/trunk/test/CodeGenObjC/ns_consume_null_check.m

Modified: cfe/trunk/lib/CodeGen/CGObjCMac.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjCMac.cpp?rev=174946&r1=174945&r2=174946&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjCMac.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjCMac.cpp Mon Feb 11 23:53:35 2013
@@ -1547,16 +1547,18 @@ public:
 /// value.
 struct NullReturnState {
   llvm::BasicBlock *NullBB;
-  llvm::BasicBlock *callBB;
-  NullReturnState() : NullBB(0), callBB(0) {}
+  NullReturnState() : NullBB(0) {}
 
+  /// Perform a null-check of the given receiver.
   void init(CodeGenFunction &CGF, llvm::Value *receiver) {
-    // Make blocks for the null-init and call edges.
-    NullBB = CGF.createBasicBlock("msgSend.nullinit");
-    callBB = CGF.createBasicBlock("msgSend.call");
+    // Make blocks for the null-receiver and call edges.
+    NullBB = CGF.createBasicBlock("msgSend.null-receiver");
+    llvm::BasicBlock *callBB = CGF.createBasicBlock("msgSend.call");
 
     // Check for a null receiver and, if there is one, jump to the
-    // null-init test.
+    // null-receiver block.  There's no point in trying to avoid it:
+    // we're always going to put *something* there, because otherwise
+    // we shouldn't have done this null-check in the first place.
     llvm::Value *isNull = CGF.Builder.CreateIsNull(receiver);
     CGF.Builder.CreateCondBr(isNull, NullBB, callBB);
 
@@ -1564,25 +1566,29 @@ struct NullReturnState {
     CGF.EmitBlock(callBB);
   }
 
+  /// Complete the null-return operation.  It is valid to call this
+  /// regardless of whether 'init' has been called.
   RValue complete(CodeGenFunction &CGF, RValue result, QualType resultType,
                   const CallArgList &CallArgs,
                   const ObjCMethodDecl *Method) {
+    // If we never had to do a null-check, just use the raw result.
     if (!NullBB) return result;
-    
-    llvm::Value *NullInitPtr = 0;
-    if (result.isScalar() && !resultType->isVoidType()) {
-      NullInitPtr = CGF.CreateTempAlloca(result.getScalarVal()->getType());
-      CGF.Builder.CreateStore(result.getScalarVal(), NullInitPtr);
-    }
 
+    // The continuation block.  This will be left null if we don't have an
+    // IP, which can happen if the method we're calling is marked noreturn.
+    llvm::BasicBlock *contBB = 0;
+    
     // Finish the call path.
-    llvm::BasicBlock *contBB = CGF.createBasicBlock("msgSend.cont");
-    if (CGF.HaveInsertPoint()) CGF.Builder.CreateBr(contBB);
+    llvm::BasicBlock *callBB = CGF.Builder.GetInsertBlock();
+    if (callBB) {
+      contBB = CGF.createBasicBlock("msgSend.cont");
+      CGF.Builder.CreateBr(contBB);
+    }
 
-    // Emit the null-init block and perform the null-initialization there.
+    // Okay, start emitting the null-receiver block.
     CGF.EmitBlock(NullBB);
     
-    // Release consumed arguments along the null-receiver path.
+    // Release any consumed arguments we've got.
     if (Method) {
       CallArgList::const_iterator I = CallArgs.begin();
       for (ObjCMethodDecl::param_const_iterator i = Method->param_begin(),
@@ -1596,39 +1602,60 @@ struct NullReturnState {
         }
       }
     }
-    
+
+    // The phi code below assumes that we haven't needed any control flow yet.
+    assert(CGF.Builder.GetInsertBlock() == NullBB);
+
+    // If we've got a void return, just jump to the continuation block.
+    if (result.isScalar() && resultType->isVoidType()) {
+      // No jumps required if the message-send was noreturn.
+      if (contBB) CGF.EmitBlock(contBB);
+      return result;
+    }
+
+    // If we've got a scalar return, build a phi.
     if (result.isScalar()) {
-      if (NullInitPtr)
-        CGF.EmitNullInitialization(NullInitPtr, resultType);
-      // Jump to the continuation block.
+      // Derive the null-initialization value.
+      llvm::Constant *null = CGF.CGM.EmitNullConstant(resultType);
+
+      // If no join is necessary, just flow out.
+      if (!contBB) return RValue::get(null);
+
+      // Otherwise, build a phi.
       CGF.EmitBlock(contBB);
-      return NullInitPtr ? RValue::get(CGF.Builder.CreateLoad(NullInitPtr)) 
-      : result;
+      llvm::PHINode *phi = CGF.Builder.CreatePHI(null->getType(), 2);
+      phi->addIncoming(result.getScalarVal(), callBB);
+      phi->addIncoming(null, NullBB);
+      return RValue::get(phi);
     }
-    
-    if (!resultType->isAnyComplexType()) {
+
+    // If we've got an aggregate return, null the buffer out.
+    // FIXME: maybe we should be doing things differently for all the
+    // cases where the ABI has us returning (1) non-agg values in
+    // memory or (2) agg values in registers.
+    if (result.isAggregate()) {
       assert(result.isAggregate() && "null init of non-aggregate result?");
       CGF.EmitNullInitialization(result.getAggregateAddr(), resultType);
-      // Jump to the continuation block.
-      CGF.EmitBlock(contBB);
+      if (contBB) CGF.EmitBlock(contBB);
       return result;
     }
 
-    // _Complex type
-    // FIXME. Now easy to handle any other scalar type whose result is returned
-    // in memory due to ABI limitations.
+    // Complex types.
     CGF.EmitBlock(contBB);
-    CodeGenFunction::ComplexPairTy CallCV = result.getComplexVal();
-    llvm::Type *MemberType = CallCV.first->getType();
-    llvm::Constant *ZeroCV = llvm::Constant::getNullValue(MemberType);
-    // Create phi instruction for scalar complex value.
-    llvm::PHINode *PHIReal = CGF.Builder.CreatePHI(MemberType, 2);
-    PHIReal->addIncoming(ZeroCV, NullBB);
-    PHIReal->addIncoming(CallCV.first, callBB);
-    llvm::PHINode *PHIImag = CGF.Builder.CreatePHI(MemberType, 2);
-    PHIImag->addIncoming(ZeroCV, NullBB);
-    PHIImag->addIncoming(CallCV.second, callBB);
-    return RValue::getComplex(PHIReal, PHIImag);
+    CodeGenFunction::ComplexPairTy callResult = result.getComplexVal();
+
+    // Find the scalar type and its zero value.
+    llvm::Type *scalarTy = callResult.first->getType();
+    llvm::Constant *scalarZero = llvm::Constant::getNullValue(scalarTy);
+
+    // Build phis for both coordinates.
+    llvm::PHINode *real = CGF.Builder.CreatePHI(scalarTy, 2);
+    real->addIncoming(callResult.first, callBB);
+    real->addIncoming(scalarZero, NullBB);
+    llvm::PHINode *imag = CGF.Builder.CreatePHI(scalarTy, 2);
+    imag->addIncoming(callResult.second, callBB);
+    imag->addIncoming(scalarZero, NullBB);
+    return RValue::getComplex(real, imag);
   }
 };
 

Modified: cfe/trunk/test/CodeGenObjC/complex-double-abi.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/complex-double-abi.m?rev=174946&r1=174945&r2=174946&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/complex-double-abi.m (original)
+++ cfe/trunk/test/CodeGenObjC/complex-double-abi.m Mon Feb 11 23:53:35 2013
@@ -9,8 +9,7 @@ double _Complex foo(CNumber *x) {
   return [x sum];
 }
 
-// CHECK: [[T4:%.*]] = phi double [ 0.000000e+00, [[NULLINIT:%.*]] ], [ [[R1:%.*]], [[MSGCALL:%.*]] ]
-// CHECK: [[T5:%.*]] = phi double [ 0.000000e+00, [[NULLINIT:%.*]] ], [ [[I1:%.*]], [[MSGCALL:%.*]] ]
-
-// CHECK: store double [[T4]]
-// CHECK: store double [[T5]]
+// CHECK:      [[R:%.*]] = phi double [ [[R1:%.*]], [[MSGCALL:%.*]] ], [ 0.000000e+00, [[NULLINIT:%.*]] ]
+// CHECK-NEXT: [[I:%.*]] = phi double [ [[I1:%.*]], [[MSGCALL]] ], [ 0.000000e+00, [[NULLINIT]] ]
+// CHECK: store double [[R]]
+// CHECK: store double [[I]]

Modified: cfe/trunk/test/CodeGenObjC/ns_consume_null_check.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenObjC/ns_consume_null_check.m?rev=174946&r1=174945&r2=174946&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenObjC/ns_consume_null_check.m (original)
+++ cfe/trunk/test/CodeGenObjC/ns_consume_null_check.m Mon Feb 11 23:53:35 2013
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-dispatch-method=mixed -o - %s | FileCheck %s
-// rdar://10444476
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-dispatch-method=mixed -fobjc-runtime-has-weak -fexceptions -o - %s | FileCheck %s
 
 @interface NSObject
 - (id) new;
@@ -7,26 +6,78 @@
 
 @interface MyObject : NSObject
 - (char)isEqual:(id) __attribute__((ns_consumed)) object;
+- (_Complex float) asComplexWithArg: (id) __attribute__((ns_consumed)) object;
 @end
 
 MyObject *x;
 
-void foo()
-{
-        id obj = [NSObject new];
-        [x isEqual : obj];
+// rdar://10444476
+void test0(void) {
+  id obj = [NSObject new];
+  [x isEqual : obj];
 }
-
-// CHECK: [[TMP:%.*]] = alloca i8{{$}}
-// CHECK: [[FIVE:%.*]] = call i8* @objc_retain
+// CHECK:     define void @test0()
+// CHECK:       [[FIVE:%.*]] = call i8* @objc_retain
 // CHECK-NEXT:  [[SIX:%.*]] = bitcast
 // CHECK-NEXT:  [[SEVEN:%.*]]  = icmp eq i8* [[SIX]], null
 // CHECK-NEXT:  br i1 [[SEVEN]], label [[NULLINIT:%.*]], label [[CALL_LABEL:%.*]]
-// CHECK:  [[FN:%.*]] = load i8** getelementptr inbounds
+// CHECK:       [[FN:%.*]] = load i8** getelementptr inbounds
 // CHECK-NEXT:  [[EIGHT:%.*]] = bitcast i8* [[FN]]
 // CHECK-NEXT:  [[CALL:%.*]] = call signext i8 [[EIGHT]]
-// CHECK-NEXT:  store i8 [[CALL]], i8* [[TMP]]
 // CHECK-NEXT:  br label [[CONT:%.*]]
-// CHECK:   call void @objc_release(i8* [[FIVE]]) nounwind
-// CHECK-NEXT:   call void @llvm.memset
+// CHECK:       call void @objc_release(i8* [[FIVE]]) nounwind
 // CHECK-NEXT:  br label [[CONT]]
+// CHECK:       phi i8 [ [[CALL]], {{%.*}} ], [ 0, {{%.*}} ]
+
+// Ensure that we build PHIs correctly in the presence of cleanups.
+// rdar://12046763
+void test1(void) {
+  id obj = [NSObject new];
+  __weak id weakObj = obj;
+  _Complex float result = [x asComplexWithArg: obj];
+}
+// CHECK:    define void @test1()
+// CHECK:      [[OBJ:%.*]] = alloca i8*, align 8
+// CHECK-NEXT: [[WEAKOBJ:%.*]] = alloca i8*, align 8
+// CHECK-NEXT: [[RESULT:%.*]] = alloca { float, float }, align 4
+//   Various initializations.
+// CHECK:      [[T0:%.*]] = call i8* bitcast (
+// CHECK-NEXT: store i8* [[T0]], i8** [[OBJ]]
+// CHECK-NEXT: [[T0:%.*]] = load i8** [[OBJ]]
+// CHECK-NEXT: call i8* @objc_initWeak(i8** [[WEAKOBJ]], i8* [[T0]]) nounwind
+//   Okay, start the message-send.
+// CHECK-NEXT: [[T0:%.*]] = load [[MYOBJECT:%.*]]** @x
+// CHECK-NEXT: [[ARG:%.*]] = load i8** [[OBJ]]
+// CHECK-NEXT: [[ARG_RETAINED:%.*]] = call i8* @objc_retain(i8* [[ARG]])
+// CHECK-NEXT: load i8** @
+// CHECK-NEXT: [[SELF:%.*]] = bitcast [[MYOBJECT]]* [[T0]] to i8*
+//   Null check.
+// CHECK-NEXT: [[T0:%.*]] = icmp eq i8* [[SELF]], null
+// CHECK-NEXT: br i1 [[T0]], label [[FORNULL:%.*]], label [[FORCALL:%.*]]
+//   Invoke and produce the return values.
+// CHECK:      [[CALL:%.*]] = invoke <2 x float> bitcast
+// CHECK-NEXT:   to label [[INVOKE_CONT:%.*]] unwind label {{%.*}}
+// CHECK:      [[T0:%.*]] = bitcast { float, float }* [[COERCE:%.*]] to <2 x float>*
+// CHECK-NEXT: store <2 x float> [[CALL]], <2 x float>* [[T0]],
+// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { float, float }* [[COERCE]], i32 0, i32 0
+// CHECK-NEXT: [[REALCALL:%.*]] = load float* [[T0]]
+// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { float, float }* [[COERCE]], i32 0, i32 1
+// CHECK-NEXT: [[IMAGCALL:%.*]] = load float* [[T0]]
+// CHECK-NEXT: br label [[CONT:%.*]]{{$}}
+//   Null path.
+// CHECK:      call void @objc_release(i8* [[ARG_RETAINED]]) nounwind
+// CHECK-NEXT: br label [[CONT]]
+//   Join point.
+// CHECK:      [[REAL:%.*]] = phi float [ [[REALCALL]], [[INVOKE_CONT]] ], [ 0.000000e+00, [[FORNULL]] ]
+// CHECK-NEXT: [[IMAG:%.*]] = phi float [ [[IMAGCALL]], [[INVOKE_CONT]] ], [ 0.000000e+00, [[FORNULL]] ]
+// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds { float, float }* [[RESULT]], i32 0, i32 0
+// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds { float, float }* [[RESULT]], i32 0, i32 1
+// CHECK-NEXT: store float [[REAL]], float* [[T0]]
+// CHECK-NEXT: store float [[IMAG]], float* [[T1]]
+//   Epilogue.
+// CHECK-NEXT: call void @objc_destroyWeak(i8** [[WEAKOBJ]]) nounwind
+// CHECK-NEXT: call void @objc_storeStrong(i8** [[OBJ]], i8* null) nounwind
+// CHECK-NEXT: ret void
+//   Cleanup.
+// CHECK:      landingpad
+// CHECK:      call void @objc_destroyWeak(i8** [[WEAKOBJ]]) nounwind





More information about the cfe-commits mailing list