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