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

rdar://12046763

llvm-svn: 174946
This commit is contained in:
John McCall 2013-02-12 05:53:35 +00:00
parent 7c85c9433c
commit 3d1e2c960d
3 changed files with 140 additions and 63 deletions

View File

@ -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(),
@ -1597,38 +1603,59 @@ struct NullReturnState {
}
}
if (result.isScalar()) {
if (NullInitPtr)
CGF.EmitNullInitialization(NullInitPtr, resultType);
// Jump to the continuation block.
CGF.EmitBlock(contBB);
return NullInitPtr ? RValue::get(CGF.Builder.CreateLoad(NullInitPtr))
: result;
}
// The phi code below assumes that we haven't needed any control flow yet.
assert(CGF.Builder.GetInsertBlock() == NullBB);
if (!resultType->isAnyComplexType()) {
assert(result.isAggregate() && "null init of non-aggregate result?");
CGF.EmitNullInitialization(result.getAggregateAddr(), resultType);
// Jump to the continuation block.
CGF.EmitBlock(contBB);
// 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;
}
// _Complex type
// FIXME. Now easy to handle any other scalar type whose result is returned
// in memory due to ABI limitations.
// If we've got a scalar return, build a phi.
if (result.isScalar()) {
// 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);
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);
llvm::PHINode *phi = CGF.Builder.CreatePHI(null->getType(), 2);
phi->addIncoming(result.getScalarVal(), callBB);
phi->addIncoming(null, NullBB);
return RValue::get(phi);
}
// 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);
if (contBB) CGF.EmitBlock(contBB);
return result;
}
// Complex types.
CGF.EmitBlock(contBB);
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);
}
};

View File

@ -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]]

View File

@ -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,17 +6,17 @@
@interface MyObject : NSObject
- (char)isEqual:(id) __attribute__((ns_consumed)) object;
- (_Complex float) asComplexWithArg: (id) __attribute__((ns_consumed)) object;
@end
MyObject *x;
void foo()
{
// rdar://10444476
void test0(void) {
id obj = [NSObject new];
[x isEqual : obj];
}
// CHECK: [[TMP:%.*]] = alloca i8{{$}}
// CHECK: define void @test0()
// CHECK: [[FIVE:%.*]] = call i8* @objc_retain
// CHECK-NEXT: [[SIX:%.*]] = bitcast
// CHECK-NEXT: [[SEVEN:%.*]] = icmp eq i8* [[SIX]], null
@ -25,8 +24,60 @@ void foo()
// 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-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