[ObjCARC Annotations] Added support for displaying the state of pointers at the bottom/top of BBs of the ARC dataflow analysis for both bottomup and topdown analyses.

This will allow for verification and analysis of the merge function of
the data flow analyses in the ARC optimizer.

The actual implementation of this feature is by introducing calls to
the functions llvm.arc.annotation.{bottomup,topdown}.{bbstart,bbend}
which are only declared. Each such call takes in a pointer to a global
with the same name as the pointer whose provenance is being tracked and
a pointer whose name is one of our Sequence states and points to a
string that contains the same name.

To ensure that the optimizer does not consider these annotations in any
way, I made it so that the annotations are considered to be of IC_None
type.

A test case is included for this commit and the previous
ObjCARCAnnotation commit.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@177952 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Michael Gottesman 2013-03-26 00:42:09 +00:00
parent 7bef073622
commit 26dbfb6a78
3 changed files with 453 additions and 4 deletions

View File

@ -33,6 +33,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Debug.h"
@ -763,14 +764,18 @@ static MDString *AppendMDNodeToSourcePtr(unsigned NodeId,
return Hash;
}
static std::string SequenceToString(Sequence A) {
std::string str;
raw_string_ostream os(str);
os << A;
return os.str();
}
/// Helper function to change a Sequence into a String object using our overload
/// for raw_ostream so we only have printing code in one location.
static MDString *SequenceToMDString(LLVMContext &Context,
Sequence A) {
std::string str;
raw_string_ostream os(str);
os << A;
return MDString::get(Context, os.str());
return MDString::get(Context, SequenceToString(A));
}
/// A simple function to generate a MDNode which describes the change in state
@ -793,6 +798,79 @@ static void AppendMDNodeToInstForPtr(unsigned NodeId,
Inst->setMetadata(NodeId, Node);
}
/// Add to the beginning of the basic block llvm.ptr.annotations which show the
/// state of a pointer at the entrance to a basic block.
static void GenerateARCBBEntranceAnnotation(const char *Name, BasicBlock *BB,
Value *Ptr, Sequence Seq) {
Module *M = BB->getParent()->getParent();
LLVMContext &C = M->getContext();
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *I8XX = PointerType::getUnqual(I8X);
Type *Params[] = {I8XX, I8XX};
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C),
ArrayRef<Type*>(Params, 2),
/*isVarArg=*/false);
Constant *Callee = M->getOrInsertFunction(Name, FTy);
IRBuilder<> Builder(BB, BB->getFirstInsertionPt());
Value *PtrName;
StringRef Tmp = Ptr->getName();
if (0 == (PtrName = M->getGlobalVariable(Tmp, true))) {
Value *ActualPtrName = Builder.CreateGlobalStringPtr(Tmp,
Tmp + "_STR");
PtrName = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
cast<Constant>(ActualPtrName), Tmp);
}
Value *S;
std::string SeqStr = SequenceToString(Seq);
if (0 == (S = M->getGlobalVariable(SeqStr, true))) {
Value *ActualPtrName = Builder.CreateGlobalStringPtr(SeqStr,
SeqStr + "_STR");
S = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
cast<Constant>(ActualPtrName), SeqStr);
}
Builder.CreateCall2(Callee, PtrName, S);
}
/// Add to the end of the basic block llvm.ptr.annotations which show the state
/// of the pointer at the bottom of the basic block.
static void GenerateARCBBTerminatorAnnotation(const char *Name, BasicBlock *BB,
Value *Ptr, Sequence Seq) {
Module *M = BB->getParent()->getParent();
LLVMContext &C = M->getContext();
Type *I8X = PointerType::getUnqual(Type::getInt8Ty(C));
Type *I8XX = PointerType::getUnqual(I8X);
Type *Params[] = {I8XX, I8XX};
FunctionType *FTy = FunctionType::get(Type::getVoidTy(C),
ArrayRef<Type*>(Params, 2),
/*isVarArg=*/false);
Constant *Callee = M->getOrInsertFunction(Name, FTy);
IRBuilder<> Builder(BB, llvm::prior(BB->end()));
Value *PtrName;
StringRef Tmp = Ptr->getName();
if (0 == (PtrName = M->getGlobalVariable(Tmp, true))) {
Value *ActualPtrName = Builder.CreateGlobalStringPtr(Tmp,
Tmp + "_STR");
PtrName = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
cast<Constant>(ActualPtrName), Tmp);
}
Value *S;
std::string SeqStr = SequenceToString(Seq);
if (0 == (S = M->getGlobalVariable(SeqStr, true))) {
Value *ActualPtrName = Builder.CreateGlobalStringPtr(SeqStr,
SeqStr + "_STR");
S = new GlobalVariable(*M, I8X, true, GlobalVariable::InternalLinkage,
cast<Constant>(ActualPtrName), SeqStr);
}
Builder.CreateCall2(Callee, PtrName, S);
}
/// Adds a source annotation to pointer and a state change annotation to Inst
/// referencing the source annotation and the old/new state of pointer.
static void GenerateARCAnnotation(unsigned InstMDId,
@ -1818,6 +1896,21 @@ ObjCARCOpt::VisitBottomUp(BasicBlock *BB,
}
}
#ifdef ARC_ANNOTATIONS
if (EnableARCAnnotations) {
// If ARC Annotations are enabled, output the current state of pointers at the
// bottom of the basic block.
for(BBState::ptr_const_iterator I = MyStates.bottom_up_ptr_begin(),
E = MyStates.bottom_up_ptr_end(); I != E; ++I) {
Value *Ptr = const_cast<Value*>(I->first);
Sequence Seq = I->second.GetSeq();
GenerateARCBBTerminatorAnnotation("llvm.arc.annotation.bottomup.bbend",
BB, Ptr, Seq);
}
}
#endif
// Visit all the instructions, bottom-up.
for (BasicBlock::iterator I = BB->end(), E = BB->begin(); I != E; --I) {
Instruction *Inst = llvm::prior(I);
@ -1841,6 +1934,20 @@ ObjCARCOpt::VisitBottomUp(BasicBlock *BB,
NestingDetected |= VisitInstructionBottomUp(II, BB, Retains, MyStates);
}
#ifdef ARC_ANNOTATIONS
if (EnableARCAnnotations) {
// If ARC Annotations are enabled, output the current state of pointers at the
// top of the basic block.
for(BBState::ptr_const_iterator I = MyStates.bottom_up_ptr_begin(),
E = MyStates.bottom_up_ptr_end(); I != E; ++I) {
Value *Ptr = const_cast<Value*>(I->first);
Sequence Seq = I->second.GetSeq();
GenerateARCBBEntranceAnnotation("llvm.arc.annotation.bottomup.bbstart",
BB, Ptr, Seq);
}
}
#endif
return NestingDetected;
}
@ -2012,6 +2119,20 @@ ObjCARCOpt::VisitTopDown(BasicBlock *BB,
}
}
#ifdef ARC_ANNOTATIONS
if (EnableARCAnnotations) {
// If ARC Annotations are enabled, output the current state of pointers at the
// top of the basic block.
for(BBState::ptr_const_iterator I = MyStates.top_down_ptr_begin(),
E = MyStates.top_down_ptr_end(); I != E; ++I) {
Value *Ptr = const_cast<Value*>(I->first);
Sequence Seq = I->second.GetSeq();
GenerateARCBBEntranceAnnotation("llvm.arc.annotation.topdown.bbstart",
BB, Ptr, Seq);
}
}
#endif
// Visit all the instructions, top-down.
for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ++I) {
Instruction *Inst = I;
@ -2021,6 +2142,20 @@ ObjCARCOpt::VisitTopDown(BasicBlock *BB,
NestingDetected |= VisitInstructionTopDown(Inst, Releases, MyStates);
}
#ifdef ARC_ANNOTATIONS
if (EnableARCAnnotations) {
// If ARC Annotations are enabled, output the current state of pointers at the
// bottom of the basic block.
for(BBState::ptr_const_iterator I = MyStates.top_down_ptr_begin(),
E = MyStates.top_down_ptr_end(); I != E; ++I) {
Value *Ptr = const_cast<Value*>(I->first);
Sequence Seq = I->second.GetSeq();
GenerateARCBBTerminatorAnnotation("llvm.arc.annotation.topdown.bbend",
BB, Ptr, Seq);
}
}
#endif
CheckForCFGHazards(BB, BBStates, MyStates);
return NestingDetected;
}

View File

@ -145,6 +145,14 @@ InstructionClass llvm::objcarc::GetFunctionClass(const Function *F) {
return StringSwitch<InstructionClass>(F->getName())
.Case("objc_moveWeak", IC_MoveWeak)
.Case("objc_copyWeak", IC_CopyWeak)
// Ignore annotation calls. This is important to stop the
// optimizer from treating annotations as uses which would
// make the state of the pointers they are attempting to
// elucidate to be incorrect.
.Case("llvm.arc.annotation.topdown.bbstart", IC_None)
.Case("llvm.arc.annotation.topdown.bbend", IC_None)
.Case("llvm.arc.annotation.bottomup.bbstart", IC_None)
.Case("llvm.arc.annotation.bottomup.bbend", IC_None)
.Default(IC_CallOrUser);
}

View File

@ -0,0 +1,306 @@
; This file consists of various tests which ensure that the objc-arc-annotations
; are working correctly. In the future, I will use this in other lit tests to
; check the data flow analysis of ARC.
; RUN: opt -S -objc-arc -enable-objc-arc-annotations < %s | FileCheck %s
declare i8* @objc_retain(i8*)
declare i8* @objc_retainAutoreleasedReturnValue(i8*)
declare void @objc_release(i8*)
declare i8* @objc_autorelease(i8*)
declare i8* @objc_autoreleaseReturnValue(i8*)
declare void @objc_autoreleasePoolPop(i8*)
declare i8* @objc_autoreleasePoolPush()
declare i8* @objc_retainBlock(i8*)
declare i8* @objc_retainedObject(i8*)
declare i8* @objc_unretainedObject(i8*)
declare i8* @objc_unretainedPointer(i8*)
declare void @use_pointer(i8*)
declare void @callee()
declare void @callee_fnptr(void ()*)
declare void @invokee()
declare i8* @returner()
; Simple retain+release pair deletion, with some intervening control
; flow and harmless instructions.
; CHECK: define void @test0(
; CHECK: entry:
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None)
; CHECK: %0 = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !0, !llvm.arc.annotation.topdown !1
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Use)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: t:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use)
; CHECK: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup !2
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: f:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use)
; CHECK: store i32 7, i32* %x, !llvm.arc.annotation.bottomup !2
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: return:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release)
; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !3, !llvm.arc.annotation.topdown !4
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: }
define void @test0(i32* %x, i1 %p) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
br i1 %p, label %t, label %f
t:
store i8 3, i8* %a
%b = bitcast i32* %x to float*
store float 2.0, float* %b
br label %return
f:
store i32 7, i32* %x
br label %return
return:
%c = bitcast i32* %x to i8*
call void @objc_release(i8* %c) nounwind
ret void
}
; Like test0 but the release isn't always executed when the retain is,
; so the optimization is not safe.
; TODO: Make the objc_release's argument be %0.
; CHECK: define void @test1(
; CHECK: entry:
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None)
; CHECK: %0 = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !5, !llvm.arc.annotation.topdown !6
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: t:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use)
; CHECK: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup !7
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: f:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None)
; CHECK: call void @callee(), !llvm.arc.annotation.topdown !8
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_CanRelease)
; CHECK: return:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release)
; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !9
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: alt_return:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: }
define void @test1(i32* %x, i1 %p, i1 %q) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
br i1 %p, label %t, label %f
t:
store i8 3, i8* %a
%b = bitcast i32* %x to float*
store float 2.0, float* %b
br label %return
f:
store i32 7, i32* %x
call void @callee()
br i1 %q, label %return, label %alt_return
return:
%c = bitcast i32* %x to i8*
call void @objc_release(i8* %c) nounwind
ret void
alt_return:
ret void
}
; Don't do partial elimination into two different CFG diamonds.
; CHECK: define void @test1b(
; CHECK: entry:
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None)
; CHECK: %0 = tail call i8* @objc_retain(i8* %x) #0, !llvm.arc.annotation.bottomup !10, !llvm.arc.annotation.topdown !11
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: if.then:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_CanRelease)
; CHECK: tail call void @callee(), !llvm.arc.annotation.bottomup !12, !llvm.arc.annotation.topdown !13
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Use)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_CanRelease)
; CHECK: if.end:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_CanRelease)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use)
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Use)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_CanRelease)
; CHECK: if.then3:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_CanRelease)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use)
; CHECK: tail call void @use_pointer(i8* %x), !llvm.arc.annotation.bottomup !14, !llvm.arc.annotation.topdown !15
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_MovableRelease)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Use)
; CHECK: if.end5:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_MovableRelease)
; CHECK: tail call void @objc_release(i8* %x) #0, !clang.imprecise_release !16, !llvm.arc.annotation.bottomup !17
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: }
define void @test1b(i8* %x, i1 %p, i1 %q) {
entry:
tail call i8* @objc_retain(i8* %x) nounwind
br i1 %p, label %if.then, label %if.end
if.then: ; preds = %entry
tail call void @callee()
br label %if.end
if.end: ; preds = %if.then, %entry
br i1 %q, label %if.then3, label %if.end5
if.then3: ; preds = %if.end
tail call void @use_pointer(i8* %x)
br label %if.end5
if.end5: ; preds = %if.then3, %if.end
tail call void @objc_release(i8* %x) nounwind, !clang.imprecise_release !0
ret void
}
; Like test0 but the pointer is passed to an intervening call,
; so the optimization is not safe.
; CHECK: define void @test2(
; CHECK: entry:
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None)
; CHECK: %e = tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !18, !llvm.arc.annotation.topdown !19
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_CanRelease)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: t:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Use)
; CHECK: store float 2.000000e+00, float* %b, !llvm.arc.annotation.bottomup !20
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: f:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_CanRelease)
; CHECK: call void @use_pointer(i8* %e), !llvm.arc.annotation.bottomup !21, !llvm.arc.annotation.topdown !22
; CHECK: store float 3.000000e+00, float* %d, !llvm.arc.annotation.bottomup !20, !llvm.arc.annotation.topdown !23
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Use)
; CHECK: return:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Use)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release)
; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !24, !llvm.arc.annotation.topdown !25
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: }
define void @test2(i32* %x, i1 %p) nounwind {
entry:
%a = bitcast i32* %x to i8*
%e = call i8* @objc_retain(i8* %a) nounwind
br i1 %p, label %t, label %f
t:
store i8 3, i8* %a
%b = bitcast i32* %x to float*
store float 2.0, float* %b
br label %return
f:
store i32 7, i32* %x
call void @use_pointer(i8* %e)
%d = bitcast i32* %x to float*
store float 3.0, float* %d
br label %return
return:
%c = bitcast i32* %x to i8*
call void @objc_release(i8* %c) nounwind
ret void
}
; Like test0 but the release is in a loop,
; so the optimization is not safe.
; TODO: For now, assume this can't happen.
; CHECK: define void @test3(
; CHECK: entry:
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_None)
; CHECK: tail call i8* @objc_retain(i8* %a) #0, !llvm.arc.annotation.bottomup !26, !llvm.arc.annotation.topdown !27
; CHECK: call void @llvm.arc.annotation.bottomup.bbend(i8** @x, i8** @S_Release)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_Retain)
; CHECK: loop:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_Retain)
; CHECK: call void @llvm.arc.annotation.bottomup.bbstart(i8** @x, i8** @S_Release)
; CHECK: call void @objc_release(i8* %c) #0, !llvm.arc.annotation.bottomup !28, !llvm.arc.annotation.topdown !29
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: return:
; CHECK: call void @llvm.arc.annotation.topdown.bbstart(i8** @x, i8** @S_None)
; CHECK: call void @llvm.arc.annotation.topdown.bbend(i8** @x, i8** @S_None)
; CHECK: }
define void @test3(i32* %x, i1* %q) nounwind {
entry:
%a = bitcast i32* %x to i8*
%0 = call i8* @objc_retain(i8* %a) nounwind
br label %loop
loop:
%c = bitcast i32* %x to i8*
call void @objc_release(i8* %c) nounwind
%j = load volatile i1* %q
br i1 %j, label %loop, label %return
return:
ret void
}
!0 = metadata !{}
; CHECK: !0 = metadata !{metadata !"(test0,%x)", metadata !"S_Use", metadata !"S_None"}
; CHECK: !1 = metadata !{metadata !"(test0,%x)", metadata !"S_None", metadata !"S_Retain"}
; CHECK: !2 = metadata !{metadata !"(test0,%x)", metadata !"S_Release", metadata !"S_Use"}
; CHECK: !3 = metadata !{metadata !"(test0,%x)", metadata !"S_None", metadata !"S_Release"}
; CHECK: !4 = metadata !{metadata !"(test0,%x)", metadata !"S_Retain", metadata !"S_None"}
; CHECK: !5 = metadata !{metadata !"(test1,%x)", metadata !"S_None", metadata !"S_None"}
; CHECK: !6 = metadata !{metadata !"(test1,%x)", metadata !"S_None", metadata !"S_Retain"}
; CHECK: !7 = metadata !{metadata !"(test1,%x)", metadata !"S_Release", metadata !"S_Use"}
; CHECK: !8 = metadata !{metadata !"(test1,%x)", metadata !"S_Retain", metadata !"S_CanRelease"}
; CHECK: !9 = metadata !{metadata !"(test1,%x)", metadata !"S_None", metadata !"S_Release"}
; CHECK: !10 = metadata !{metadata !"(test1b,%x)", metadata !"S_None", metadata !"S_None"}
; CHECK: !11 = metadata !{metadata !"(test1b,%x)", metadata !"S_None", metadata !"S_Retain"}
; CHECK: !12 = metadata !{metadata !"(test1b,%x)", metadata !"S_Use", metadata !"S_CanRelease"}
; CHECK: !13 = metadata !{metadata !"(test1b,%x)", metadata !"S_Retain", metadata !"S_CanRelease"}
; CHECK: !14 = metadata !{metadata !"(test1b,%x)", metadata !"S_MovableRelease", metadata !"S_Use"}
; CHECK: !15 = metadata !{metadata !"(test1b,%x)", metadata !"S_CanRelease", metadata !"S_Use"}
; CHECK: !16 = metadata !{}
; CHECK: !17 = metadata !{metadata !"(test1b,%x)", metadata !"S_None", metadata !"S_MovableRelease"}
; CHECK: !18 = metadata !{metadata !"(test2,%x)", metadata !"S_CanRelease", metadata !"S_None"}
; CHECK: !19 = metadata !{metadata !"(test2,%x)", metadata !"S_None", metadata !"S_Retain"}
; CHECK: !20 = metadata !{metadata !"(test2,%x)", metadata !"S_Release", metadata !"S_Use"}
; CHECK: !21 = metadata !{metadata !"(test2,%x)", metadata !"S_Use", metadata !"S_CanRelease"}
; CHECK: !22 = metadata !{metadata !"(test2,%x)", metadata !"S_Retain", metadata !"S_CanRelease"}
; CHECK: !23 = metadata !{metadata !"(test2,%x)", metadata !"S_CanRelease", metadata !"S_Use"}
; CHECK: !24 = metadata !{metadata !"(test2,%x)", metadata !"S_None", metadata !"S_Release"}
; CHECK: !25 = metadata !{metadata !"(test2,%x)", metadata !"S_Use", metadata !"S_None"}
; CHECK: !26 = metadata !{metadata !"(test3,%x)", metadata !"S_Release", metadata !"S_None"}
; CHECK: !27 = metadata !{metadata !"(test3,%x)", metadata !"S_None", metadata !"S_Retain"}
; CHECK: !28 = metadata !{metadata !"(test3,%x)", metadata !"S_None", metadata !"S_Release"}
; CHECK: !29 = metadata !{metadata !"(test3,%x)", metadata !"S_Retain", metadata !"S_None"}