When emitting an ARC epilogue that looks like a return of 'self',

kill the retain from the return site.

This has the workaround nature.  It's badness all around.

llvm-svn: 149193
This commit is contained in:
John McCall 2012-01-29 07:46:59 +00:00
parent bbb032e219
commit ffa2c1a48e
2 changed files with 67 additions and 0 deletions

View File

@ -1100,6 +1100,17 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
assert(AI == Fn->arg_end() && "Argument mismatch!");
}
static void eraseUnusedBitCasts(llvm::Instruction *insn) {
while (insn->use_empty()) {
llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(insn);
if (!bitcast) return;
// This is "safe" because we would have used a ConstantExpr otherwise.
insn = cast<llvm::Instruction>(bitcast->getOperand(0));
bitcast->eraseFromParent();
}
}
/// Try to emit a fused autorelease of a return result.
static llvm::Value *tryEmitFusedAutoreleaseOfResult(CodeGenFunction &CGF,
llvm::Value *result) {
@ -1178,9 +1189,54 @@ static llvm::Value *tryEmitFusedAutoreleaseOfResult(CodeGenFunction &CGF,
return CGF.Builder.CreateBitCast(result, resultType);
}
/// If this is a +1 of the value of an immutable 'self', remove it.
static llvm::Value *tryRemoveRetainOfSelf(CodeGenFunction &CGF,
llvm::Value *result) {
// This is only applicable to a method with an immutable 'self'.
const ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CGF.CurCodeDecl);
if (!method) return 0;
const VarDecl *self = method->getSelfDecl();
if (!self->getType().isConstQualified()) return 0;
// Look for a retain call.
llvm::CallInst *retainCall =
dyn_cast<llvm::CallInst>(result->stripPointerCasts());
if (!retainCall ||
retainCall->getCalledValue() != CGF.CGM.getARCEntrypoints().objc_retain)
return 0;
// Look for an ordinary load of 'self'.
llvm::Value *retainedValue = retainCall->getArgOperand(0);
llvm::LoadInst *load =
dyn_cast<llvm::LoadInst>(retainedValue->stripPointerCasts());
if (!load || load->isAtomic() || load->isVolatile() ||
load->getPointerOperand() != CGF.GetAddrOfLocalVar(self))
return 0;
// Okay! Burn it all down. This relies for correctness on the
// assumption that the retain is emitted as part of the return and
// that thereafter everything is used "linearly".
llvm::Type *resultType = result->getType();
eraseUnusedBitCasts(cast<llvm::Instruction>(result));
assert(retainCall->use_empty());
retainCall->eraseFromParent();
eraseUnusedBitCasts(cast<llvm::Instruction>(retainedValue));
return CGF.Builder.CreateBitCast(load, resultType);
}
/// Emit an ARC autorelease of the result of a function.
///
/// \return the value to actually return from the function
static llvm::Value *emitAutoreleaseOfResult(CodeGenFunction &CGF,
llvm::Value *result) {
// If we're returning 'self', kill the initial retain. This is a
// heuristic attempt to "encourage correctness" in the really unfortunate
// case where we have a return of self during a dealloc and we desperately
// need to avoid the possible autorelease.
if (llvm::Value *self = tryRemoveRetainOfSelf(CGF, result))
return self;
// At -O0, try to emit a fused retain/autorelease.
if (CGF.shouldUseFusedARCCalls())
if (llvm::Value *fused = tryEmitFusedAutoreleaseOfResult(CGF, result))

View File

@ -1521,3 +1521,14 @@ void test68(void) {
// CHECK-NEXT: [[T2:%.*]] = load i8** [[CL]]
// CHECK-NEXT: call void @objc_release(i8* [[T2]])
// CHECK-NEXT: ret void
// rdar://problem/10564852
@interface Test69 @end
@implementation Test69
- (id) foo { return self; }
@end
// CHECK: define internal i8* @"\01-[Test69 foo]"(
// CHECK: [[SELF:%.*]] = alloca [[TEST69:%.*]]*, align 8
// CHECK: [[T0:%.*]] = load [[TEST69]]** [[SELF]], align 8
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST69]]* [[T0]] to i8*
// CHECK-NEXT: ret i8* [[T1]]