]> granicus.if.org Git - clang/commitdiff
When emitting an ARC epilogue that looks like a return of 'self',
authorJohn McCall <rjmccall@apple.com>
Sun, 29 Jan 2012 07:46:59 +0000 (07:46 +0000)
committerJohn McCall <rjmccall@apple.com>
Sun, 29 Jan 2012 07:46:59 +0000 (07:46 +0000)
kill the retain from the return site.

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

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149193 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGCall.cpp
test/CodeGenObjC/arc.m

index 940cee3bbd277a6328de856841741d2fc51c2c6f..f3e788ba473bc28290c788bb8d587686076a9be4 100644 (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))
index 1a020e2a52911dd42f72d81de5db0d04c4ffb6bb..e367c8c19da75c29677c1de5acf5052315e00b7a 100644 (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]]