forked from OSchip/llvm-project
Properly assemble PHIs after a null-checked invoke of objc_msgSend.
rdar://12046763 llvm-svn: 174946
This commit is contained in:
parent
7c85c9433c
commit
3d1e2c960d
|
@ -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;
|
||||
|
||||
// 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;
|
||||
|
||||
llvm::Value *NullInitPtr = 0;
|
||||
if (result.isScalar() && !resultType->isVoidType()) {
|
||||
NullInitPtr = CGF.CreateTempAlloca(result.getScalarVal()->getType());
|
||||
CGF.Builder.CreateStore(result.getScalarVal(), NullInitPtr);
|
||||
// Finish the call path.
|
||||
llvm::BasicBlock *callBB = CGF.Builder.GetInsertBlock();
|
||||
if (callBB) {
|
||||
contBB = CGF.createBasicBlock("msgSend.cont");
|
||||
CGF.Builder.CreateBr(contBB);
|
||||
}
|
||||
|
||||
// Finish the call path.
|
||||
llvm::BasicBlock *contBB = CGF.createBasicBlock("msgSend.cont");
|
||||
if (CGF.HaveInsertPoint()) 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 {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// _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);
|
||||
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 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue