[llvm-commits] [llvm] r140814 - in /llvm/trunk: lib/Transforms/Scalar/ObjCARC.cpp test/Transforms/ObjCARC/retain-block-alloca.ll

Dan Gohman gohman at apple.com
Thu Sep 29 15:25:23 PDT 2011


Author: djg
Date: Thu Sep 29 17:25:23 2011
New Revision: 140814

URL: http://llvm.org/viewvc/llvm-project?rev=140814&view=rev
Log:
Don't eliminate objc_retainBlock calls on stack objects if the
objc_retainBlock call is potentially responsible for copying
the block to the heap to extend its lifetime. rdar://10209613.

Added:
    llvm/trunk/test/Transforms/ObjCARC/retain-block-alloca.ll
Modified:
    llvm/trunk/lib/Transforms/Scalar/ObjCARC.cpp

Modified: llvm/trunk/lib/Transforms/Scalar/ObjCARC.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/ObjCARC.cpp?rev=140814&r1=140813&r2=140814&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Scalar/ObjCARC.cpp (original)
+++ llvm/trunk/lib/Transforms/Scalar/ObjCARC.cpp Thu Sep 29 17:25:23 2011
@@ -2338,6 +2338,11 @@
       S.SetAtLeastOneRefCount();
       S.DecrementNestCount();
 
+      // An objc_retainBlock call with just a use still needs to be kept,
+      // because it may be copying a block from the stack to the heap.
+      if (Class == IC_RetainBlock && S.GetSeq() == S_Use)
+        S.SetSeq(S_CanRelease);
+
       switch (S.GetSeq()) {
       case S_Stop:
       case S_Release:
@@ -2406,14 +2411,14 @@
       case S_Release:
       case S_MovableRelease:
         if (CanUse(Inst, Ptr, PA, Class)) {
-          S.RRI.ReverseInsertPts.clear();
+          assert(S.RRI.ReverseInsertPts.empty());
           S.RRI.ReverseInsertPts.insert(Inst);
           S.SetSeq(S_Use);
         } else if (Seq == S_Release &&
                    (Class == IC_User || Class == IC_CallOrUser)) {
           // Non-movable releases depend on any possible objc pointer use.
           S.SetSeq(S_Stop);
-          S.RRI.ReverseInsertPts.clear();
+          assert(S.RRI.ReverseInsertPts.empty());
           S.RRI.ReverseInsertPts.insert(Inst);
         }
         break;
@@ -2566,7 +2571,7 @@
         switch (Seq) {
         case S_Retain:
           S.SetSeq(S_CanRelease);
-          S.RRI.ReverseInsertPts.clear();
+          assert(S.RRI.ReverseInsertPts.empty());
           S.RRI.ReverseInsertPts.insert(Inst);
 
           // One call can't cause a transition from S_Retain to S_CanRelease
@@ -2590,8 +2595,18 @@
         if (CanUse(Inst, Ptr, PA, Class))
           S.SetSeq(S_Use);
         break;
-      case S_Use:
       case S_Retain:
+        // An objc_retainBlock call may be responsible for copying the block
+        // data from the stack to the heap. Model this by moving it straight
+        // from S_Retain to S_Use.
+        if (S.RRI.IsRetainBlock &&
+            CanUse(Inst, Ptr, PA, Class)) {
+          assert(S.RRI.ReverseInsertPts.empty());
+          S.RRI.ReverseInsertPts.insert(Inst);
+          S.SetSeq(S_Use);
+        }
+        break;
+      case S_Use:
       case S_None:
         break;
       case S_Stop:
@@ -2743,17 +2758,23 @@
   SmallVector<Instruction *, 8> DeadInsts;
 
   for (MapVector<Value *, RRInfo>::const_iterator I = Retains.begin(),
-       E = Retains.end(); I != E; ) {
-    Value *V = (I++)->first;
+       E = Retains.end(); I != E; ++I) {
+    Value *V = I->first;
     if (!V) continue; // blotted
 
     Instruction *Retain = cast<Instruction>(V);
     Value *Arg = GetObjCArg(Retain);
 
-    // If the object being released is in static or stack storage, we know it's
+    // If the object being released is in static storage, we know it's
     // not being managed by ObjC reference counting, so we can delete pairs
     // regardless of what possible decrements or uses lie between them.
-    bool KnownSafe = isa<Constant>(Arg) || isa<AllocaInst>(Arg);
+    bool KnownSafe = isa<Constant>(Arg);
+   
+    // Same for stack storage, unless this is an objc_retainBlock call,
+    // which is responsible for copying the block data from the stack to
+    // the heap.
+    if (!I->second.IsRetainBlock && isa<AllocaInst>(Arg))
+      KnownSafe = true;
 
     // A constant pointer can't be pointing to an object on the heap. It may
     // be reference-counted, but it won't be deleted.

Added: llvm/trunk/test/Transforms/ObjCARC/retain-block-alloca.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/ObjCARC/retain-block-alloca.ll?rev=140814&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/ObjCARC/retain-block-alloca.ll (added)
+++ llvm/trunk/test/Transforms/ObjCARC/retain-block-alloca.ll Thu Sep 29 17:25:23 2011
@@ -0,0 +1,54 @@
+; RUN: opt -S -objc-arc < %s | FileCheck %s
+; rdar://10209613
+
+; CHECK: define void @test
+; CHECK: %3 = call i8* @objc_retainBlock(i8* %2) nounwind
+; CHECK: @objc_msgSend
+; CHECK-NEXT: @objc_release(i8* %3)
+
+%0 = type opaque
+%struct.__block_descriptor = type { i64, i64 }
+
+ at _NSConcreteStackBlock = external global i8*
+ at __block_descriptor_tmp = external hidden constant { i64, i64, i8*, i8*, i8*, i8* }
+@"\01L_OBJC_SELECTOR_REFERENCES_" = external hidden global i8*, section "__DATA, __objc_selrefs, literal_pointers, no_dead_strip"
+
+define void @test(%0* %array) uwtable {
+entry:
+  %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, align 8
+  %0 = bitcast %0* %array to i8*
+  %1 = tail call i8* @objc_retain(i8* %0) nounwind
+  %block.isa = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 0
+  store i8* bitcast (i8** @_NSConcreteStackBlock to i8*), i8** %block.isa, align 8
+  %block.flags = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 1
+  store i32 1107296256, i32* %block.flags, align 8
+  %block.reserved = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 2
+  store i32 0, i32* %block.reserved, align 4
+  %block.invoke = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 3
+  store i8* bitcast (void (i8*)* @__test_block_invoke_0 to i8*), i8** %block.invoke, align 8
+  %block.descriptor = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 4
+  store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @__block_descriptor_tmp to %struct.__block_descriptor*), %struct.__block_descriptor** %block.descriptor, align 8
+  %block.captured = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block, i64 0, i32 5
+  store %0* %array, %0** %block.captured, align 8
+  %2 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %block to i8*
+  %3 = call i8* @objc_retainBlock(i8* %2) nounwind
+  %tmp2 = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_", align 8
+  call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i8*)*)(i8* %0, i8* %tmp2, i8* %3)
+  call void @objc_release(i8* %3) nounwind
+  %strongdestroy = load %0** %block.captured, align 8
+  %4 = bitcast %0* %strongdestroy to i8*
+  call void @objc_release(i8* %4) nounwind, !clang.imprecise_release !0
+  ret void
+}
+
+declare i8* @objc_retain(i8*)
+
+declare void @__test_block_invoke_0(i8* nocapture) uwtable
+
+declare i8* @objc_retainBlock(i8*)
+
+declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind
+
+declare void @objc_release(i8*)
+
+!0 = metadata !{}





More information about the llvm-commits mailing list