forked from OSchip/llvm-project
Revert "[ObjC][ARC] Annotate calls with attributes instead of emitting retainRV"
This reverts commit 53176c1680
, which
introduceed a layering violation. LLVM's IR library can't include
headers from Analysis.
This commit is contained in:
parent
03f1c9fa85
commit
925ae8c790
|
@ -23,7 +23,6 @@
|
|||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/CodeGen/CGFunctionInfo.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
|
@ -2305,11 +2304,10 @@ static void emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) {
|
|||
// with this marker yet, so leave a breadcrumb for the ARC
|
||||
// optimizer to pick up.
|
||||
} else {
|
||||
const char *retainRVMarkerKey = llvm::objcarc::getRVMarkerModuleFlagStr();
|
||||
if (!CGF.CGM.getModule().getModuleFlag(retainRVMarkerKey)) {
|
||||
const char *markerKey = "clang.arc.retainAutoreleasedReturnValueMarker";
|
||||
if (!CGF.CGM.getModule().getModuleFlag(markerKey)) {
|
||||
auto *str = llvm::MDString::get(CGF.getLLVMContext(), assembly);
|
||||
CGF.CGM.getModule().addModuleFlag(llvm::Module::Error,
|
||||
retainRVMarkerKey, str);
|
||||
CGF.CGM.getModule().addModuleFlag(llvm::Module::Error, markerKey, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2319,43 +2317,6 @@ static void emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) {
|
|||
CGF.Builder.CreateCall(marker, None, CGF.getBundlesForFunclet(marker));
|
||||
}
|
||||
|
||||
static llvm::Value *emitOptimizedARCReturnCall(llvm::Value *value,
|
||||
bool IsRetainRV,
|
||||
CodeGenFunction &CGF) {
|
||||
emitAutoreleasedReturnValueMarker(CGF);
|
||||
|
||||
// Annotate the call with attributes instead of emitting retainRV or claimRV
|
||||
// calls in the IR. We currently do this only when the optimization level
|
||||
// isn't -O0 since global-isel, which is currently run at -O0, doesn't know
|
||||
// about the attributes.
|
||||
|
||||
// FIXME: Do this when the target isn't aarch64.
|
||||
if (CGF.CGM.getCodeGenOpts().OptimizationLevel > 0 &&
|
||||
CGF.CGM.getTarget().getTriple().isAArch64()) {
|
||||
auto *callBase = cast<llvm::CallBase>(value);
|
||||
llvm::AttributeList attrs = callBase->getAttributes();
|
||||
attrs = attrs.addAttribute(CGF.getLLVMContext(),
|
||||
llvm::AttributeList::ReturnIndex,
|
||||
llvm::objcarc::getRVAttrKeyStr(),
|
||||
llvm::objcarc::getRVAttrValStr(IsRetainRV));
|
||||
callBase->setAttributes(attrs);
|
||||
return callBase;
|
||||
}
|
||||
|
||||
bool isNoTail =
|
||||
CGF.CGM.getTargetCodeGenInfo().markARCOptimizedReturnCallsAsNoTail();
|
||||
llvm::CallInst::TailCallKind tailKind =
|
||||
isNoTail ? llvm::CallInst::TCK_NoTail : llvm::CallInst::TCK_None;
|
||||
ObjCEntrypoints &EPs = CGF.CGM.getObjCEntrypoints();
|
||||
llvm::Function *&EP = IsRetainRV
|
||||
? EPs.objc_retainAutoreleasedReturnValue
|
||||
: EPs.objc_unsafeClaimAutoreleasedReturnValue;
|
||||
llvm::Intrinsic::ID IID =
|
||||
IsRetainRV ? llvm::Intrinsic::objc_retainAutoreleasedReturnValue
|
||||
: llvm::Intrinsic::objc_unsafeClaimAutoreleasedReturnValue;
|
||||
return emitARCValueOperation(CGF, value, nullptr, EP, IID, tailKind);
|
||||
}
|
||||
|
||||
/// Retain the given object which is the result of a function call.
|
||||
/// call i8* \@objc_retainAutoreleasedReturnValue(i8* %value)
|
||||
///
|
||||
|
@ -2363,7 +2324,15 @@ static llvm::Value *emitOptimizedARCReturnCall(llvm::Value *value,
|
|||
/// call with completely different semantics.
|
||||
llvm::Value *
|
||||
CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) {
|
||||
return emitOptimizedARCReturnCall(value, true, *this);
|
||||
emitAutoreleasedReturnValueMarker(*this);
|
||||
llvm::CallInst::TailCallKind tailKind =
|
||||
CGM.getTargetCodeGenInfo().markARCOptimizedReturnCallsAsNoTail()
|
||||
? llvm::CallInst::TCK_NoTail
|
||||
: llvm::CallInst::TCK_None;
|
||||
return emitARCValueOperation(
|
||||
*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_retainAutoreleasedReturnValue,
|
||||
llvm::Intrinsic::objc_retainAutoreleasedReturnValue, tailKind);
|
||||
}
|
||||
|
||||
/// Claim a possibly-autoreleased return value at +0. This is only
|
||||
|
@ -2375,7 +2344,15 @@ CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) {
|
|||
/// call i8* \@objc_unsafeClaimAutoreleasedReturnValue(i8* %value)
|
||||
llvm::Value *
|
||||
CodeGenFunction::EmitARCUnsafeClaimAutoreleasedReturnValue(llvm::Value *value) {
|
||||
return emitOptimizedARCReturnCall(value, false, *this);
|
||||
emitAutoreleasedReturnValueMarker(*this);
|
||||
llvm::CallInst::TailCallKind tailKind =
|
||||
CGM.getTargetCodeGenInfo().markARCOptimizedReturnCallsAsNoTail()
|
||||
? llvm::CallInst::TCK_NoTail
|
||||
: llvm::CallInst::TCK_None;
|
||||
return emitARCValueOperation(
|
||||
*this, value, nullptr,
|
||||
CGM.getObjCEntrypoints().objc_unsafeClaimAutoreleasedReturnValue,
|
||||
llvm::Intrinsic::objc_unsafeClaimAutoreleasedReturnValue, tailKind);
|
||||
}
|
||||
|
||||
/// Release the given object.
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -O -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK
|
||||
|
||||
@class A;
|
||||
|
||||
A *makeA(void);
|
||||
|
||||
void test_assign() {
|
||||
__unsafe_unretained id x;
|
||||
x = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_assign()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="claim" [[A:.*]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_assign_assign() {
|
||||
__unsafe_unretained id x, y;
|
||||
x = y = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_assign_assign()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[Y:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="claim" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_strong_assign_assign() {
|
||||
__strong id x;
|
||||
__unsafe_unretained id y;
|
||||
x = y = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_strong_assign_assign()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[Y:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="retain" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
||||
// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[OLD]]
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[X]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_assign_strong_assign() {
|
||||
__unsafe_unretained id x;
|
||||
__strong id y;
|
||||
x = y = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_assign_strong_assign()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[Y:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="retain" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[Y]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[OLD]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_init() {
|
||||
__unsafe_unretained id x = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_init()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="claim" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_init_assignment() {
|
||||
__unsafe_unretained id x;
|
||||
__unsafe_unretained id y = x = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_init_assignment()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[Y:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="claim" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_strong_init_assignment() {
|
||||
__unsafe_unretained id x;
|
||||
__strong id y = x = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_strong_init_assignment()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[Y:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="retain" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
||||
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_init_strong_assignment() {
|
||||
__strong id x;
|
||||
__unsafe_unretained id y = x = makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_init_strong_assignment()
|
||||
// CHECK: [[X:%.*]] = alloca i8*
|
||||
// CHECK: [[Y:%.*]] = alloca i8*
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="retain" [[A]]* @makeA()
|
||||
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
|
||||
// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]]
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[OLD]])
|
||||
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[X]]
|
||||
// CHECK-NEXT: call void @llvm.objc.release(i8* [[T0]])
|
||||
// CHECK-NEXT: bitcast
|
||||
// CHECK-NEXT: lifetime.end
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_ignored() {
|
||||
makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_ignored()
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="claim" [[A]]* @makeA()
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
void test_cast_to_void() {
|
||||
(void) makeA();
|
||||
}
|
||||
// CHECK-LABEL: define{{.*}} void @test_cast_to_void()
|
||||
// CHECK: [[T0:%.*]] = call "clang.arc.rv"="claim" [[A]]* @makeA()
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// This is always at the end of the module.
|
||||
|
||||
// CHECK-OPTIMIZED: !llvm.module.flags = !{!0,
|
||||
// CHECK-OPTIMIZED: !0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker", !"mov{{.*}}marker for objc_retainAutoreleaseReturnValue"}
|
|
@ -4,10 +4,11 @@
|
|||
// Make sure it works on x86-32.
|
||||
// RUN: %clang_cc1 -triple i386-apple-darwin11 -fobjc-runtime=macosx-fragile-10.11 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED -check-prefix=CALL
|
||||
|
||||
// Make sure it works on ARM64.
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED -check-prefix=CALL
|
||||
|
||||
// Make sure it works on ARM.
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED -check-prefix=CALL
|
||||
// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -O -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-OPTIMIZED -check-prefix=CALL
|
||||
|
||||
// Make sure it works on ARM64.
|
||||
// RUN: %clang_cc1 -triple armv7-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED -check-prefix=CALL
|
||||
// RUN: %clang_cc1 -triple armv7-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -O -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-OPTIMIZED -check-prefix=CALL
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
//===- ObjCARCRVAttr.h - ObjC ARC Attribute Analysis ------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// This file defines functions which look for or remove attributes retainRV,
|
||||
/// claimRV, and rv_marker.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_ANALYSIS_OBJCARCRVATTR_H
|
||||
#define LLVM_LIB_ANALYSIS_OBJCARCRVATTR_H
|
||||
|
||||
#include "llvm/IR/InstrTypes.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace objcarc {
|
||||
|
||||
static inline const char *getRVMarkerModuleFlagStr() {
|
||||
return "clang.arc.retainAutoreleasedReturnValueMarker";
|
||||
}
|
||||
|
||||
static inline const char *getRVAttrKeyStr() { return "clang.arc.rv"; }
|
||||
|
||||
static inline const char *getRVAttrValStr(bool Retain) {
|
||||
return Retain ? "retain" : "claim";
|
||||
}
|
||||
|
||||
static inline bool hasRetainRVAttr(const CallBase *CB) {
|
||||
return CB->getAttribute(llvm::AttributeList::ReturnIndex, getRVAttrKeyStr())
|
||||
.getValueAsString()
|
||||
.equals(getRVAttrValStr(true));
|
||||
}
|
||||
static inline bool hasClaimRVAttr(const CallBase *CB) {
|
||||
return CB->getAttribute(llvm::AttributeList::ReturnIndex, getRVAttrKeyStr())
|
||||
.getValueAsString()
|
||||
.equals(getRVAttrValStr(false));
|
||||
}
|
||||
|
||||
static inline bool hasRetainRVOrClaimRVAttr(const CallBase *CB) {
|
||||
return CB->hasRetAttr(getRVAttrKeyStr());
|
||||
}
|
||||
|
||||
static inline void removeRetainRVOrClaimRVAttr(CallBase *CB) {
|
||||
CB->removeAttribute(llvm::AttributeList::ReturnIndex, getRVAttrKeyStr());
|
||||
}
|
||||
|
||||
} // end namespace objcarc
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
|
@ -14,15 +14,14 @@
|
|||
|
||||
#include "llvm/IR/AutoUpgrade.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DIBuilder.h"
|
||||
#include "llvm/IR/DebugInfo.h"
|
||||
#include "llvm/IR/DiagnosticInfo.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/InstVisitor.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/Intrinsics.h"
|
||||
#include "llvm/IR/IntrinsicsAArch64.h"
|
||||
|
@ -3997,7 +3996,7 @@ bool llvm::UpgradeDebugInfo(Module &M) {
|
|||
/// returns true if module is modified.
|
||||
static bool UpgradeRetainReleaseMarker(Module &M) {
|
||||
bool Changed = false;
|
||||
const char *MarkerKey = objcarc::getRVMarkerModuleFlagStr();
|
||||
const char *MarkerKey = "clang.arc.retainAutoreleasedReturnValueMarker";
|
||||
NamedMDNode *ModRetainReleaseMarker = M.getNamedMetadata(MarkerKey);
|
||||
if (ModRetainReleaseMarker) {
|
||||
MDNode *Op = ModRetainReleaseMarker->getOperand(0);
|
||||
|
|
|
@ -11,11 +11,10 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/IR/MDBuilder.h"
|
||||
#include "llvm/IR/Operator.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
|
@ -573,13 +572,8 @@ bool Instruction::mayWriteToMemory() const {
|
|||
return true;
|
||||
case Instruction::Call:
|
||||
case Instruction::Invoke:
|
||||
case Instruction::CallBr: {
|
||||
if (!cast<CallBase>(this)->onlyReadsMemory())
|
||||
return true;
|
||||
if (auto *CB = dyn_cast<CallBase>(this))
|
||||
return objcarc::hasRetainRVOrClaimRVAttr(CB);
|
||||
return false;
|
||||
}
|
||||
case Instruction::CallBr:
|
||||
return !cast<CallBase>(this)->onlyReadsMemory();
|
||||
case Instruction::Load:
|
||||
return !cast<LoadInst>(this)->isUnordered();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/Analysis/VectorUtils.h"
|
||||
#include "llvm/CodeGen/CallingConvLower.h"
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
|
@ -5648,11 +5647,11 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
|
|||
}
|
||||
|
||||
unsigned CallOpc = AArch64ISD::CALL;
|
||||
// Calls marked with "clang.arc.rv" are special. They should be expanded to
|
||||
// the call, directly followed by a special marker sequence. Use the
|
||||
// CALL_RVMARKER to do that.
|
||||
if (CLI.CB && CLI.CB->hasRetAttr(objcarc::getRVAttrKeyStr())) {
|
||||
assert(!IsTailCall && "tail calls cannot be marked with clang.arc.rv");
|
||||
// Calls marked with "rv_marker" are special. They should be expanded to the
|
||||
// call, directly followed by a special marker sequence. Use the CALL_RVMARKER
|
||||
// to do that.
|
||||
if (CLI.CB && CLI.CB->hasRetAttr("rv_marker")) {
|
||||
assert(!IsTailCall && "tail calls cannot be marked with rv_marker");
|
||||
CallOpc = AArch64ISD::CALL_RVMARKER;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "llvm/Transforms/IPO/DeadArgumentElimination.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/Argument.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
|
@ -572,13 +571,6 @@ void DeadArgumentEliminationPass::SurveyFunction(const Function &F) {
|
|||
if (NumLiveRetVals == RetCount)
|
||||
continue;
|
||||
|
||||
// Don't change the return type of the function if it has retainRV/claimRV.
|
||||
if (objcarc::hasRetainRVOrClaimRVAttr(CB)) {
|
||||
NumLiveRetVals = RetCount;
|
||||
RetValLiveness.assign(RetCount, Live);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check all uses of the return value.
|
||||
for (const Use &U : CB->uses()) {
|
||||
if (ExtractValueInst *Ext = dyn_cast<ExtractValueInst>(U.getUser())) {
|
||||
|
|
|
@ -42,7 +42,6 @@ enum class ARCRuntimeEntryPointKind {
|
|||
Autorelease,
|
||||
StoreStrong,
|
||||
RetainRV,
|
||||
ClaimRV,
|
||||
RetainAutorelease,
|
||||
RetainAutoreleaseRV,
|
||||
};
|
||||
|
@ -62,7 +61,6 @@ public:
|
|||
Autorelease = nullptr;
|
||||
StoreStrong = nullptr;
|
||||
RetainRV = nullptr;
|
||||
ClaimRV = nullptr;
|
||||
RetainAutorelease = nullptr;
|
||||
RetainAutoreleaseRV = nullptr;
|
||||
}
|
||||
|
@ -87,9 +85,6 @@ public:
|
|||
case ARCRuntimeEntryPointKind::RetainRV:
|
||||
return getIntrinsicEntryPoint(RetainRV,
|
||||
Intrinsic::objc_retainAutoreleasedReturnValue);
|
||||
case ARCRuntimeEntryPointKind::ClaimRV:
|
||||
return getIntrinsicEntryPoint(
|
||||
ClaimRV, Intrinsic::objc_unsafeClaimAutoreleasedReturnValue);
|
||||
case ARCRuntimeEntryPointKind::RetainAutorelease:
|
||||
return getIntrinsicEntryPoint(RetainAutorelease,
|
||||
Intrinsic::objc_retainAutorelease);
|
||||
|
@ -126,9 +121,6 @@ private:
|
|||
/// Declaration for objc_retainAutoreleasedReturnValue().
|
||||
Function *RetainRV = nullptr;
|
||||
|
||||
/// Declaration for objc_unsafeClaimAutoreleasedReturnValue().
|
||||
Function *ClaimRV = nullptr;
|
||||
|
||||
/// Declaration for objc_retainAutorelease().
|
||||
Function *RetainAutorelease = nullptr;
|
||||
|
||||
|
|
|
@ -14,12 +14,7 @@
|
|||
|
||||
#include "ObjCARC.h"
|
||||
#include "llvm-c/Initialization.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/InitializePasses.h"
|
||||
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
|
||||
|
||||
namespace llvm {
|
||||
class PassRegistry;
|
||||
|
@ -42,91 +37,3 @@ void llvm::initializeObjCARCOpts(PassRegistry &Registry) {
|
|||
void LLVMInitializeObjCARCOpts(LLVMPassRegistryRef R) {
|
||||
initializeObjCARCOpts(*unwrap(R));
|
||||
}
|
||||
|
||||
CallInst *llvm::objcarc::createCallInstWithColors(
|
||||
FunctionCallee Func, ArrayRef<Value *> Args, const Twine &NameStr,
|
||||
Instruction *InsertBefore,
|
||||
const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
|
||||
FunctionType *FTy = Func.getFunctionType();
|
||||
Value *Callee = Func.getCallee();
|
||||
SmallVector<OperandBundleDef, 1> OpBundles;
|
||||
|
||||
if (!BlockColors.empty()) {
|
||||
const ColorVector &CV = BlockColors.find(InsertBefore->getParent())->second;
|
||||
assert(CV.size() == 1 && "non-unique color for block!");
|
||||
Instruction *EHPad = CV.front()->getFirstNonPHI();
|
||||
if (EHPad->isEHPad())
|
||||
OpBundles.emplace_back("funclet", EHPad);
|
||||
}
|
||||
|
||||
return CallInst::Create(FTy, Callee, Args, OpBundles, NameStr, InsertBefore);
|
||||
}
|
||||
|
||||
std::pair<bool, bool>
|
||||
BundledRetainClaimRVs::insertAfterInvokes(Function &F, DominatorTree *DT) {
|
||||
bool Changed = false, CFGChanged = false;
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
auto *I = dyn_cast<InvokeInst>(BB.getTerminator());
|
||||
|
||||
if (!I)
|
||||
continue;
|
||||
|
||||
if (!objcarc::hasRetainRVOrClaimRVAttr(I))
|
||||
continue;
|
||||
|
||||
BasicBlock *DestBB = I->getNormalDest();
|
||||
|
||||
if (!DestBB->getSinglePredecessor()) {
|
||||
assert(I->getSuccessor(0) == DestBB &&
|
||||
"the normal dest is expected to be the first successor");
|
||||
DestBB = llvm::SplitCriticalEdge(I, 0, CriticalEdgeSplittingOptions(DT));
|
||||
CFGChanged = true;
|
||||
}
|
||||
|
||||
// We don't have to call insertRVCallWithColors since DestBB is the normal
|
||||
// destination of the invoke.
|
||||
insertRVCall(&*DestBB->getFirstInsertionPt(), I);
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
return std::make_pair(Changed, CFGChanged);
|
||||
}
|
||||
|
||||
CallInst *BundledRetainClaimRVs::insertRVCall(Instruction *InsertPt,
|
||||
CallBase *AnnotatedCall) {
|
||||
DenseMap<BasicBlock *, ColorVector> BlockColors;
|
||||
return insertRVCallWithColors(InsertPt, AnnotatedCall, BlockColors);
|
||||
}
|
||||
|
||||
CallInst *BundledRetainClaimRVs::insertRVCallWithColors(
|
||||
Instruction *InsertPt, CallBase *AnnotatedCall,
|
||||
const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
|
||||
IRBuilder<> Builder(InsertPt);
|
||||
bool IsRetainRV = objcarc::hasRetainRVAttr(AnnotatedCall);
|
||||
Function *Func = EP.get(IsRetainRV ? ARCRuntimeEntryPointKind::RetainRV
|
||||
: ARCRuntimeEntryPointKind::ClaimRV);
|
||||
Type *ParamTy = Func->getArg(0)->getType();
|
||||
Value *CallArg = Builder.CreateBitCast(AnnotatedCall, ParamTy);
|
||||
auto *Call =
|
||||
createCallInstWithColors(Func, CallArg, "", InsertPt, BlockColors);
|
||||
RVCalls[Call] = AnnotatedCall;
|
||||
return Call;
|
||||
}
|
||||
|
||||
BundledRetainClaimRVs::~BundledRetainClaimRVs() {
|
||||
if (ContractPass) {
|
||||
// At this point, we know that the annotated calls can't be tail calls as
|
||||
// they are followed by marker instructions and retainRV/claimRV calls. Mark
|
||||
// them as notail, so that the backend knows these calls can't be tail
|
||||
// calls.
|
||||
for (auto P : RVCalls)
|
||||
if (auto *CI = dyn_cast<CallInst>(P.second))
|
||||
CI->setTailCallKind(CallInst::TCK_NoTail);
|
||||
} else {
|
||||
for (auto P : RVCalls)
|
||||
EraseInstruction(P.first);
|
||||
}
|
||||
|
||||
RVCalls.clear();
|
||||
}
|
||||
|
|
|
@ -22,10 +22,7 @@
|
|||
#ifndef LLVM_LIB_TRANSFORMS_OBJCARC_OBJCARC_H
|
||||
#define LLVM_LIB_TRANSFORMS_OBJCARC_OBJCARC_H
|
||||
|
||||
#include "ARCRuntimeEntryPoints.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#include "llvm/Analysis/ObjCARCAnalysisUtils.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
|
||||
namespace llvm {
|
||||
|
@ -90,63 +87,6 @@ void getEquivalentPHIs(PHINodeTy &PN, VectorTy &PHIList) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline MDString *getRVInstMarker(Module &M) {
|
||||
const char *MarkerKey = getRVMarkerModuleFlagStr();
|
||||
return dyn_cast_or_null<MDString>(M.getModuleFlag(MarkerKey));
|
||||
}
|
||||
|
||||
/// Create a call instruction with the correct funclet token. This should be
|
||||
/// called instead of calling CallInst::Create directly unless the call is
|
||||
/// going to be removed from the IR before WinEHPrepare.
|
||||
CallInst *createCallInstWithColors(
|
||||
FunctionCallee Func, ArrayRef<Value *> Args, const Twine &NameStr,
|
||||
Instruction *InsertBefore,
|
||||
const DenseMap<BasicBlock *, ColorVector> &BlockColors);
|
||||
|
||||
class BundledRetainClaimRVs {
|
||||
public:
|
||||
BundledRetainClaimRVs(ARCRuntimeEntryPoints &P, bool ContractPass)
|
||||
: EP(P), ContractPass(ContractPass) {}
|
||||
~BundledRetainClaimRVs();
|
||||
|
||||
/// Insert a retainRV/claimRV call to the normal destination blocks of invokes
|
||||
/// annotated with retainRV/claimRV. If the edge to the normal destination
|
||||
/// block is a critical edge, split it.
|
||||
std::pair<bool, bool> insertAfterInvokes(Function &F, DominatorTree *DT);
|
||||
|
||||
/// Insert a retainRV/claimRV call.
|
||||
CallInst *insertRVCall(Instruction *InsertPt, CallBase *AnnotatedCall);
|
||||
|
||||
/// Insert a retainRV/claimRV call with colors.
|
||||
CallInst *insertRVCallWithColors(
|
||||
Instruction *InsertPt, CallBase *AnnotatedCall,
|
||||
const DenseMap<BasicBlock *, ColorVector> &BlockColors);
|
||||
|
||||
/// See if an instruction is a bundled retainRV/claimRV call.
|
||||
bool contains(const Instruction *I) const {
|
||||
if (auto *CI = dyn_cast<CallInst>(I))
|
||||
return RVCalls.count(CI);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Remove a retainRV/claimRV call entirely.
|
||||
void eraseInst(CallInst *CI) {
|
||||
auto It = RVCalls.find(CI);
|
||||
if (It != RVCalls.end()) {
|
||||
objcarc::removeRetainRVOrClaimRVAttr(It->second);
|
||||
RVCalls.erase(It);
|
||||
}
|
||||
EraseInstruction(CI);
|
||||
}
|
||||
|
||||
private:
|
||||
/// A map of inserted retainRV/claimRV calls to annotated calls/invokes.
|
||||
DenseMap<CallInst *, CallBase *> RVCalls;
|
||||
|
||||
ARCRuntimeEntryPoints &EP;
|
||||
bool ContractPass;
|
||||
};
|
||||
|
||||
} // end namespace objcarc
|
||||
} // end namespace llvm
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/InlineAsm.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
|
@ -64,12 +63,13 @@ namespace {
|
|||
|
||||
class ObjCARCContract {
|
||||
bool Changed;
|
||||
bool CFGChanged;
|
||||
AAResults *AA;
|
||||
DominatorTree *DT;
|
||||
ProvenanceAnalysis PA;
|
||||
ARCRuntimeEntryPoints EP;
|
||||
BundledRetainClaimRVs *BundledInsts = nullptr;
|
||||
|
||||
/// A flag indicating whether this optimization pass should run.
|
||||
bool Run;
|
||||
|
||||
/// The inline asm string to insert between calls and RetainRV calls to make
|
||||
/// the optimization work on targets which need it.
|
||||
|
@ -98,7 +98,6 @@ class ObjCARCContract {
|
|||
public:
|
||||
bool init(Module &M);
|
||||
bool run(Function &F, AAResults *AA, DominatorTree *DT);
|
||||
bool hasCFGChanged() const { return CFGChanged; }
|
||||
};
|
||||
|
||||
class ObjCARCContractLegacyPass : public FunctionPass {
|
||||
|
@ -305,6 +304,32 @@ findRetainForStoreStrongContraction(Value *New, StoreInst *Store,
|
|||
return Retain;
|
||||
}
|
||||
|
||||
/// Create a call instruction with the correct funclet token. Should be used
|
||||
/// instead of calling CallInst::Create directly.
|
||||
static CallInst *
|
||||
createCallInst(FunctionType *FTy, Value *Func, ArrayRef<Value *> Args,
|
||||
const Twine &NameStr, Instruction *InsertBefore,
|
||||
const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
|
||||
SmallVector<OperandBundleDef, 1> OpBundles;
|
||||
if (!BlockColors.empty()) {
|
||||
const ColorVector &CV = BlockColors.find(InsertBefore->getParent())->second;
|
||||
assert(CV.size() == 1 && "non-unique color for block!");
|
||||
Instruction *EHPad = CV.front()->getFirstNonPHI();
|
||||
if (EHPad->isEHPad())
|
||||
OpBundles.emplace_back("funclet", EHPad);
|
||||
}
|
||||
|
||||
return CallInst::Create(FTy, Func, Args, OpBundles, NameStr, InsertBefore);
|
||||
}
|
||||
|
||||
static CallInst *
|
||||
createCallInst(FunctionCallee Func, ArrayRef<Value *> Args, const Twine &NameStr,
|
||||
Instruction *InsertBefore,
|
||||
const DenseMap<BasicBlock *, ColorVector> &BlockColors) {
|
||||
return createCallInst(Func.getFunctionType(), Func.getCallee(), Args, NameStr,
|
||||
InsertBefore, BlockColors);
|
||||
}
|
||||
|
||||
/// Attempt to merge an objc_release with a store, load, and objc_retain to form
|
||||
/// an objc_storeStrong. An objc_storeStrong:
|
||||
///
|
||||
|
@ -386,8 +411,7 @@ void ObjCARCContract::tryToContractReleaseIntoStoreStrong(
|
|||
if (Args[1]->getType() != I8X)
|
||||
Args[1] = new BitCastInst(Args[1], I8X, "", Store);
|
||||
Function *Decl = EP.get(ARCRuntimeEntryPointKind::StoreStrong);
|
||||
CallInst *StoreStrong =
|
||||
objcarc::createCallInstWithColors(Decl, Args, "", Store, BlockColors);
|
||||
CallInst *StoreStrong = createCallInst(Decl, Args, "", Store, BlockColors);
|
||||
StoreStrong->setDoesNotThrow();
|
||||
StoreStrong->setDebugLoc(Store->getDebugLoc());
|
||||
|
||||
|
@ -432,14 +456,9 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
|||
case ARCInstKind::RetainRV:
|
||||
case ARCInstKind::ClaimRV: {
|
||||
// If we're compiling for a target which needs a special inline-asm
|
||||
// marker to do the return value optimization and the retainRV/claimRV call
|
||||
// wasn't bundled with a call, insert the marker now.
|
||||
// marker to do the return value optimization, insert it now.
|
||||
if (!RVInstMarker)
|
||||
return false;
|
||||
|
||||
if (BundledInsts->contains(Inst))
|
||||
return false;
|
||||
|
||||
BasicBlock::iterator BBI = Inst->getIterator();
|
||||
BasicBlock *InstParent = Inst->getParent();
|
||||
|
||||
|
@ -467,7 +486,7 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
|||
RVInstMarker->getString(),
|
||||
/*Constraints=*/"", /*hasSideEffects=*/true);
|
||||
|
||||
objcarc::createCallInstWithColors(IA, None, "", Inst, BlockColors);
|
||||
createCallInst(IA, None, "", Inst, BlockColors);
|
||||
}
|
||||
decline_rv_optimization:
|
||||
return false;
|
||||
|
@ -515,10 +534,16 @@ bool ObjCARCContract::tryToPeepholeInstruction(
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool ObjCARCContract::init(Module &M) {
|
||||
// If nothing in the Module uses ARC, don't do anything.
|
||||
Run = ModuleHasARC(M);
|
||||
if (!Run)
|
||||
return false;
|
||||
|
||||
EP.init(&M);
|
||||
|
||||
// Initialize RVInstMarker.
|
||||
RVInstMarker = getRVInstMarker(M);
|
||||
const char *MarkerKey = "clang.arc.retainAutoreleasedReturnValueMarker";
|
||||
RVInstMarker = dyn_cast_or_null<MDString>(M.getModuleFlag(MarkerKey));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -527,16 +552,14 @@ bool ObjCARCContract::run(Function &F, AAResults *A, DominatorTree *D) {
|
|||
if (!EnableARCOpts)
|
||||
return false;
|
||||
|
||||
Changed = CFGChanged = false;
|
||||
// If nothing in the Module uses ARC, don't do anything.
|
||||
if (!Run)
|
||||
return false;
|
||||
|
||||
Changed = false;
|
||||
AA = A;
|
||||
DT = D;
|
||||
PA.setAA(A);
|
||||
BundledRetainClaimRVs BRV(EP, true);
|
||||
BundledInsts = &BRV;
|
||||
|
||||
std::pair<bool, bool> R = BundledInsts->insertAfterInvokes(F, DT);
|
||||
Changed |= R.first;
|
||||
CFGChanged |= R.second;
|
||||
|
||||
DenseMap<BasicBlock *, ColorVector> BlockColors;
|
||||
if (F.hasPersonalityFn() &&
|
||||
|
@ -561,13 +584,6 @@ bool ObjCARCContract::run(Function &F, AAResults *A, DominatorTree *D) {
|
|||
|
||||
LLVM_DEBUG(dbgs() << "Visiting: " << *Inst << "\n");
|
||||
|
||||
if (auto *CI = dyn_cast<CallInst>(Inst))
|
||||
if (objcarc::hasRetainRVOrClaimRVAttr(CI)) {
|
||||
BundledInsts->insertRVCallWithColors(&*I, CI, BlockColors);
|
||||
--I;
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
// First try to peephole Inst. If there is nothing further we can do in
|
||||
// terms of undoing objc-arc-expand, process the next inst.
|
||||
if (tryToPeepholeInstruction(F, Inst, I, TailOkForStoreStrongs,
|
||||
|
@ -717,6 +733,7 @@ INITIALIZE_PASS_END(ObjCARCContractLegacyPass, "objc-arc-contract",
|
|||
void ObjCARCContractLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<AAResultsWrapperPass>();
|
||||
AU.addRequired<DominatorTreeWrapperPass>();
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
Pass *llvm::createObjCARCContractPass() {
|
||||
|
@ -740,11 +757,9 @@ PreservedAnalyses ObjCARCContractPass::run(Function &F,
|
|||
|
||||
bool Changed = OCAC.run(F, &AM.getResult<AAManager>(F),
|
||||
&AM.getResult<DominatorTreeAnalysis>(F));
|
||||
bool CFGChanged = OCAC.hasCFGChanged();
|
||||
if (Changed) {
|
||||
PreservedAnalyses PA;
|
||||
if (!CFGChanged)
|
||||
PA.preserveSet<CFGAnalyses>();
|
||||
PA.preserveSet<CFGAnalyses>();
|
||||
return PA;
|
||||
}
|
||||
return PreservedAnalyses::all();
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include "llvm/Analysis/ObjCARCAliasAnalysis.h"
|
||||
#include "llvm/Analysis/ObjCARCAnalysisUtils.h"
|
||||
#include "llvm/Analysis/ObjCARCInstKind.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/Constant.h"
|
||||
|
@ -484,7 +483,6 @@ namespace {
|
|||
/// The main ARC optimization pass.
|
||||
class ObjCARCOpt {
|
||||
bool Changed;
|
||||
bool CFGChanged;
|
||||
ProvenanceAnalysis PA;
|
||||
|
||||
/// A cache of references to runtime entry point constants.
|
||||
|
@ -494,7 +492,8 @@ class ObjCARCOpt {
|
|||
/// MDKind identifiers.
|
||||
ARCMDKindCache MDKindCache;
|
||||
|
||||
BundledRetainClaimRVs *BundledInsts = nullptr;
|
||||
/// A flag indicating whether this optimization pass should run.
|
||||
bool Run;
|
||||
|
||||
/// A flag indicating whether the optimization that removes or moves
|
||||
/// retain/release pairs should be performed.
|
||||
|
@ -574,7 +573,6 @@ class ObjCARCOpt {
|
|||
void init(Module &M);
|
||||
bool run(Function &F, AAResults &AA);
|
||||
void releaseMemory();
|
||||
bool hasCFGChanged() const { return CFGChanged; }
|
||||
};
|
||||
|
||||
/// The main ARC optimization pass.
|
||||
|
@ -612,6 +610,8 @@ Pass *llvm::createObjCARCOptPass() { return new ObjCARCOptLegacyPass(); }
|
|||
void ObjCARCOptLegacyPass::getAnalysisUsage(AnalysisUsage &AU) const {
|
||||
AU.addRequired<ObjCARCAAWrapperPass>();
|
||||
AU.addRequired<AAResultsWrapperPass>();
|
||||
// ARC optimization doesn't currently split critical edges.
|
||||
AU.setPreservesCFG();
|
||||
}
|
||||
|
||||
/// Turn objc_retainAutoreleasedReturnValue into objc_retain if the operand is
|
||||
|
@ -640,9 +640,6 @@ ObjCARCOpt::OptimizeRetainRVCall(Function &F, Instruction *RetainRV) {
|
|||
}
|
||||
}
|
||||
|
||||
assert(!BundledInsts->contains(RetainRV) &&
|
||||
"a bundled retainRV's argument should be a call");
|
||||
|
||||
// Turn it to a plain objc_retain.
|
||||
Changed = true;
|
||||
++NumPeeps;
|
||||
|
@ -664,9 +661,6 @@ bool ObjCARCOpt::OptimizeInlinedAutoreleaseRVCall(
|
|||
Function &F, DenseMap<BasicBlock *, ColorVector> &BlockColors,
|
||||
Instruction *Inst, const Value *&Arg, ARCInstKind Class,
|
||||
Instruction *AutoreleaseRV, const Value *&AutoreleaseRVArg) {
|
||||
if (BundledInsts->contains(Inst))
|
||||
return false;
|
||||
|
||||
// Must be in the same basic block.
|
||||
assert(Inst->getParent() == AutoreleaseRV->getParent());
|
||||
|
||||
|
@ -850,12 +844,6 @@ void ObjCARCOpt::OptimizeIndividualCalls(Function &F) {
|
|||
for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) {
|
||||
Instruction *Inst = &*I++;
|
||||
|
||||
if (auto *CI = dyn_cast<CallInst>(Inst))
|
||||
if (objcarc::hasRetainRVOrClaimRVAttr(CI)) {
|
||||
BundledInsts->insertRVCall(&*I, CI);
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
ARCInstKind Class = GetBasicARCInstKind(Inst);
|
||||
|
||||
// Skip this loop if this instruction isn't itself an ARC intrinsic.
|
||||
|
@ -934,11 +922,6 @@ void ObjCARCOpt::OptimizeIndividualCallImpl(
|
|||
// We can delete this call if it takes an inert value.
|
||||
SmallPtrSet<Value *, 1> VisitedPhis;
|
||||
|
||||
if (BundledInsts->contains(Inst)) {
|
||||
UsedInThisFunction |= 1 << unsigned(Class);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsNoopOnGlobal(Class))
|
||||
if (isInertARCValue(Inst->getOperand(0), VisitedPhis)) {
|
||||
if (!Inst->getType()->isVoidTy())
|
||||
|
@ -1559,7 +1542,7 @@ ObjCARCOpt::VisitInstructionTopDown(Instruction *Inst,
|
|||
if (Ptr == Arg)
|
||||
continue; // Handled above.
|
||||
TopDownPtrState &S = MI->second;
|
||||
if (S.HandlePotentialAlterRefCount(Inst, Ptr, PA, Class, *BundledInsts))
|
||||
if (S.HandlePotentialAlterRefCount(Inst, Ptr, PA, Class))
|
||||
continue;
|
||||
|
||||
S.HandlePotentialUse(Inst, Ptr, PA, Class);
|
||||
|
@ -2360,7 +2343,7 @@ void ObjCARCOpt::OptimizeReturns(Function &F) {
|
|||
++NumRets;
|
||||
LLVM_DEBUG(dbgs() << "Erasing: " << *Retain << "\nErasing: " << *Autorelease
|
||||
<< "\n");
|
||||
BundledInsts->eraseInst(Retain);
|
||||
EraseInstruction(Retain);
|
||||
EraseInstruction(Autorelease);
|
||||
}
|
||||
}
|
||||
|
@ -2393,6 +2376,11 @@ void ObjCARCOpt::init(Module &M) {
|
|||
if (!EnableARCOpts)
|
||||
return;
|
||||
|
||||
// If nothing in the Module uses ARC, don't do anything.
|
||||
Run = ModuleHasARC(M);
|
||||
if (!Run)
|
||||
return;
|
||||
|
||||
// Intuitively, objc_retain and others are nocapture, however in practice
|
||||
// they are not, because they return their argument value. And objc_release
|
||||
// calls finalizers which can have arbitrary side effects.
|
||||
|
@ -2406,18 +2394,16 @@ bool ObjCARCOpt::run(Function &F, AAResults &AA) {
|
|||
if (!EnableARCOpts)
|
||||
return false;
|
||||
|
||||
Changed = CFGChanged = false;
|
||||
BundledRetainClaimRVs BRV(EP, false);
|
||||
BundledInsts = &BRV;
|
||||
// If nothing in the Module uses ARC, don't do anything.
|
||||
if (!Run)
|
||||
return false;
|
||||
|
||||
Changed = false;
|
||||
|
||||
LLVM_DEBUG(dbgs() << "<<< ObjCARCOpt: Visiting Function: " << F.getName()
|
||||
<< " >>>"
|
||||
"\n");
|
||||
|
||||
std::pair<bool, bool> R = BundledInsts->insertAfterInvokes(F, nullptr);
|
||||
Changed |= R.first;
|
||||
CFGChanged |= R.second;
|
||||
|
||||
PA.setAA(&AA);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -2482,11 +2468,9 @@ PreservedAnalyses ObjCARCOptPass::run(Function &F,
|
|||
OCAO.init(*F.getParent());
|
||||
|
||||
bool Changed = OCAO.run(F, AM.getResult<AAManager>(F));
|
||||
bool CFGChanged = OCAO.hasCFGChanged();
|
||||
if (Changed) {
|
||||
PreservedAnalyses PA;
|
||||
if (!CFGChanged)
|
||||
PA.preserveSet<CFGAnalyses>();
|
||||
PA.preserveSet<CFGAnalyses>();
|
||||
return PA;
|
||||
}
|
||||
return PreservedAnalyses::all();
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "ObjCARC.h"
|
||||
#include "llvm/Analysis/ObjCARCAnalysisUtils.h"
|
||||
#include "llvm/Analysis/ObjCARCInstKind.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Instruction.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
|
@ -281,12 +280,6 @@ void BottomUpPtrState::HandlePotentialUse(BasicBlock *BB, Instruction *Inst,
|
|||
InsertAfter = skipDebugIntrinsics(InsertAfter);
|
||||
|
||||
InsertReverseInsertPt(&*InsertAfter);
|
||||
|
||||
// Don't insert anything between a call/invoke annotated with
|
||||
// retainRV/claimRV and the retainRV/claimRV call that uses the call result.
|
||||
if (auto *CB = dyn_cast<CallBase>(Inst))
|
||||
if (objcarc::hasRetainRVOrClaimRVAttr(CB))
|
||||
SetCFGHazardAfflicted(true);
|
||||
};
|
||||
|
||||
// Check for possible direct uses.
|
||||
|
@ -384,9 +377,10 @@ bool TopDownPtrState::MatchWithRelease(ARCMDKindCache &Cache,
|
|||
llvm_unreachable("Sequence unknown enum value");
|
||||
}
|
||||
|
||||
bool TopDownPtrState::HandlePotentialAlterRefCount(
|
||||
Instruction *Inst, const Value *Ptr, ProvenanceAnalysis &PA,
|
||||
ARCInstKind Class, const BundledRetainClaimRVs &BundledRVs) {
|
||||
bool TopDownPtrState::HandlePotentialAlterRefCount(Instruction *Inst,
|
||||
const Value *Ptr,
|
||||
ProvenanceAnalysis &PA,
|
||||
ARCInstKind Class) {
|
||||
// Check for possible releases. Treat clang.arc.use as a releasing instruction
|
||||
// to prevent sinking a retain past it.
|
||||
if (!CanDecrementRefCount(Inst, Ptr, PA, Class) &&
|
||||
|
@ -402,11 +396,6 @@ bool TopDownPtrState::HandlePotentialAlterRefCount(
|
|||
assert(!HasReverseInsertPts());
|
||||
InsertReverseInsertPt(Inst);
|
||||
|
||||
// Don't insert anything between a call/invoke annotated with
|
||||
// retainRV/claimRV and the retainRV/claimRV call that uses the call result.
|
||||
if (BundledRVs.contains(Inst))
|
||||
SetCFGHazardAfflicted(true);
|
||||
|
||||
// One call can't cause a transition from S_Retain to S_CanRelease
|
||||
// and S_CanRelease to S_Use. If we've made the first transition,
|
||||
// we're done.
|
||||
|
|
|
@ -31,7 +31,6 @@ class Value;
|
|||
namespace objcarc {
|
||||
|
||||
class ARCMDKindCache;
|
||||
class BundledRetainClaimRVs;
|
||||
class ProvenanceAnalysis;
|
||||
|
||||
/// \enum Sequence
|
||||
|
@ -203,8 +202,7 @@ struct TopDownPtrState : PtrState {
|
|||
ProvenanceAnalysis &PA, ARCInstKind Class);
|
||||
|
||||
bool HandlePotentialAlterRefCount(Instruction *Inst, const Value *Ptr,
|
||||
ProvenanceAnalysis &PA, ARCInstKind Class,
|
||||
const BundledRetainClaimRVs &BundledRVs);
|
||||
ProvenanceAnalysis &PA, ARCInstKind Class);
|
||||
};
|
||||
|
||||
} // end namespace objcarc
|
||||
|
|
|
@ -27,9 +27,8 @@
|
|||
#include "llvm/Analysis/CaptureTracking.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#include "llvm/Analysis/InstructionSimplify.h"
|
||||
#include "llvm/Analysis/ObjCARCAnalysisUtils.h"
|
||||
#include "llvm/Analysis/ObjCARCRVAttr.h"
|
||||
#include "llvm/Analysis/ProfileSummaryInfo.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/Analysis/VectorUtils.h"
|
||||
#include "llvm/IR/Argument.h"
|
||||
|
@ -62,7 +61,6 @@
|
|||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
|
||||
#include "llvm/Transforms/Utils/Cloning.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "llvm/Transforms/Utils/ValueMapper.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
@ -1646,77 +1644,6 @@ void llvm::updateProfileCallee(
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
insertRetainOrClaimRVCalls(CallBase &CB,
|
||||
const SmallVectorImpl<ReturnInst *> &Returns) {
|
||||
Module *Mod = CB.getParent()->getParent()->getParent();
|
||||
bool IsRetainRV = objcarc::hasRetainRVAttr(&CB), IsClaimRV = !IsRetainRV;
|
||||
|
||||
for (auto *RI : Returns) {
|
||||
Value *RetOpnd = llvm::objcarc::GetRCIdentityRoot(RI->getOperand(0));
|
||||
BasicBlock::reverse_iterator I = ++(RI->getIterator().getReverse());
|
||||
BasicBlock::reverse_iterator EI = RI->getParent()->rend();
|
||||
bool InsertRetainCall = IsRetainRV;
|
||||
IRBuilder<> Builder(RI->getContext());
|
||||
|
||||
// Walk backwards through the basic block looking for either a matching
|
||||
// autoreleaseRV call or an unannotated call.
|
||||
for (; I != EI;) {
|
||||
auto CurI = I++;
|
||||
|
||||
// Ignore casts.
|
||||
if (isa<CastInst>(*CurI))
|
||||
continue;
|
||||
|
||||
if (auto *II = dyn_cast<IntrinsicInst>(&*CurI)) {
|
||||
if (II->getIntrinsicID() == Intrinsic::objc_autoreleaseReturnValue &&
|
||||
II->hasNUses(0) &&
|
||||
llvm::objcarc::GetRCIdentityRoot(II->getOperand(0)) == RetOpnd) {
|
||||
// If we've found a matching authoreleaseRV call:
|
||||
// - If the call is annotated with claimRV, insert a call to
|
||||
// objc_release and erase the autoreleaseRV call.
|
||||
// - If the call is annotated with retainRV, just erase the
|
||||
// autoreleaseRV call.
|
||||
if (IsClaimRV) {
|
||||
Builder.SetInsertPoint(II);
|
||||
Function *IFn =
|
||||
Intrinsic::getDeclaration(Mod, Intrinsic::objc_release);
|
||||
Value *BC =
|
||||
Builder.CreateBitCast(RetOpnd, IFn->getArg(0)->getType());
|
||||
Builder.CreateCall(IFn, BC, "");
|
||||
}
|
||||
II->eraseFromParent();
|
||||
InsertRetainCall = false;
|
||||
}
|
||||
} else if (auto *CI = dyn_cast<CallInst>(&*CurI)) {
|
||||
if (llvm::objcarc::GetRCIdentityRoot(CI) == RetOpnd &&
|
||||
!objcarc::hasRetainRVOrClaimRVAttr(CI)) {
|
||||
// If we've found an unannotated call that defines RetOpnd, annotate
|
||||
// the call with the attributes.
|
||||
llvm::AttributeList AL = CI->getAttributes();
|
||||
AL = AL.addAttribute(CI->getContext(), AttributeList::ReturnIndex,
|
||||
objcarc::getRVAttrKeyStr(),
|
||||
objcarc::getRVAttrValStr(IsRetainRV));
|
||||
CI->setAttributes(AL);
|
||||
InsertRetainCall = false;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (InsertRetainCall) {
|
||||
// The call is annotated with retainRV and we've failed to find a matching
|
||||
// autoreleaseRV or an annotated call in the callee. Emit a call to
|
||||
// objc_retain.
|
||||
Builder.SetInsertPoint(RI);
|
||||
Function *IFn = Intrinsic::getDeclaration(Mod, Intrinsic::objc_retain);
|
||||
Value *BC = Builder.CreateBitCast(RetOpnd, IFn->getArg(0)->getType());
|
||||
Builder.CreateCall(IFn, BC, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function inlines the called function into the basic block of the
|
||||
/// caller. This returns false if it is not possible to inline this call.
|
||||
/// The program is still in a well defined state if this occurs though.
|
||||
|
@ -1920,10 +1847,6 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
|
|||
// Remember the first block that is newly cloned over.
|
||||
FirstNewBlock = LastBlock; ++FirstNewBlock;
|
||||
|
||||
// Insert retainRV/clainRV runtime calls.
|
||||
if (objcarc::hasRetainRVOrClaimRVAttr(&CB))
|
||||
insertRetainOrClaimRVCalls(CB, Returns);
|
||||
|
||||
if (IFI.CallerBFI != nullptr && IFI.CalleeBFI != nullptr)
|
||||
// Update the BFI of blocks cloned into the caller.
|
||||
updateCallerBFI(OrigBB, VMap, IFI.CallerBFI, IFI.CalleeBFI,
|
||||
|
|
|
@ -33,7 +33,7 @@ define dso_local i8* @rv_marker_1() {
|
|||
; GISEL-NOT: mov x29, x29
|
||||
;
|
||||
entry:
|
||||
%call = call "clang.arc.rv"="retain" i8* @foo1()
|
||||
%call = call "rv_marker" i8* @foo1()
|
||||
ret i8* %call
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ define dso_local void @rv_marker_2_select(i32 %c) {
|
|||
entry:
|
||||
%tobool.not = icmp eq i32 %c, 0
|
||||
%.sink = select i1 %tobool.not, i32 2, i32 1
|
||||
%call1 = call "clang.arc.rv"="retain" i8* @foo0(i32 %.sink)
|
||||
%call1 = call "rv_marker" i8* @foo0(i32 %.sink)
|
||||
tail call void @foo2(i8* %call1)
|
||||
ret void
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ define dso_local void @rv_marker_3() personality i8* bitcast (i32 (...)* @__gxx_
|
|||
; SELDAG-NEXT: mov x29, x29
|
||||
;
|
||||
entry:
|
||||
%call = call "clang.arc.rv"="retain" i8* @foo1()
|
||||
%call = call "rv_marker" i8* @foo1()
|
||||
invoke void @objc_object(i8* %call) #5
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
|
@ -87,7 +87,7 @@ entry:
|
|||
%s = alloca %struct.S, align 1
|
||||
%0 = getelementptr inbounds %struct.S, %struct.S* %s, i64 0, i32 0
|
||||
call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0) #2
|
||||
%call = invoke "clang.arc.rv"="retain" i8* @foo1()
|
||||
%call = invoke "rv_marker" i8* @foo1()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
invoke.cont: ; preds = %entry
|
||||
|
@ -127,7 +127,7 @@ define dso_local i8* @rv_marker_5_indirect_call() {
|
|||
;
|
||||
entry:
|
||||
%0 = load i8* ()*, i8* ()** @fptr, align 8
|
||||
%call = call "clang.arc.rv"="retain" i8* %0()
|
||||
%call = call "rv_marker" i8* %0()
|
||||
tail call void @foo2(i8* %call)
|
||||
ret i8* %call
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ define dso_local void @rv_marker_multiarg(i64 %a, i64 %b, i64 %c) {
|
|||
; CHECK-NEXT: bl foo
|
||||
; SELDAG-NEXT: mov x29, x29
|
||||
; GISEL-NOT: mov x29, x29
|
||||
call "clang.arc.rv"="retain" void @foo(i64 %c, i64 %b, i64 %a)
|
||||
call "rv_marker" void @foo(i64 %c, i64 %b, i64 %a)
|
||||
ret void
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
; RUN: opt < %s -deadargelim -S | FileCheck %s
|
||||
|
||||
@g0 = global i8 0, align 8
|
||||
|
||||
; CHECK-NOT: DEAD
|
||||
; RUN: opt < %s -deadargelim -S | not grep DEAD
|
||||
|
||||
; Dead arg only used by dead retval
|
||||
define internal i32 @test(i32 %DEADARG) {
|
||||
|
@ -20,19 +16,3 @@ define i32 @test3() {
|
|||
ret i32 %Y
|
||||
}
|
||||
|
||||
; The callee function's return type shouldn't be changed if there is a call
|
||||
; annotated with "clang.arc.rv"="retain" or "clang.arc.rv"="claim".
|
||||
|
||||
; CHECK-LABEL: define internal i8* @callee4()
|
||||
|
||||
define internal i8* @callee4(i8* %a0) {
|
||||
ret i8* @g0;
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define i8* @test4(
|
||||
; CHECK: tail call "clang.arc.rv"="retain" i8* @callee4()
|
||||
|
||||
define i8* @test4() {
|
||||
%call = tail call "clang.arc.rv"="retain" i8* @callee4(i8* @g0)
|
||||
ret i8* @g0
|
||||
}
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
; RUN: opt < %s -inline -S | FileCheck %s
|
||||
|
||||
@g0 = global i8* null, align 8
|
||||
declare i8* @foo0()
|
||||
|
||||
define i8* @callee0_autoreleaseRV() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @foo0()
|
||||
%1 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call)
|
||||
ret i8* %call
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test0_autoreleaseRV(
|
||||
; CHECK: call "clang.arc.rv"="retain" i8* @foo0()
|
||||
|
||||
define void @test0_autoreleaseRV() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @callee0_autoreleaseRV()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test0_claimRV_autoreleaseRV(
|
||||
; CHECK: %[[CALL:.*]] = call "clang.arc.rv"="retain" i8* @foo0()
|
||||
; CHECK: call void @llvm.objc.release(i8* %[[CALL]])
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test0_claimRV_autoreleaseRV() {
|
||||
%call = call "clang.arc.rv"="claim" i8* @callee0_autoreleaseRV()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test1_autoreleaseRV(
|
||||
; CHECK: invoke "clang.arc.rv"="retain" i8* @foo0()
|
||||
|
||||
define void @test1_autoreleaseRV() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
entry:
|
||||
%call = invoke "clang.arc.rv"="retain" i8* @callee0_autoreleaseRV()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
invoke.cont:
|
||||
ret void
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 }
|
||||
cleanup
|
||||
resume { i8*, i32 } undef
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test1_claimRV_autoreleaseRV(
|
||||
; CHECK: %[[INVOKE:.*]] = invoke "clang.arc.rv"="retain" i8* @foo0()
|
||||
; CHECK: call void @llvm.objc.release(i8* %[[INVOKE]])
|
||||
; CHECK-NEXT: br
|
||||
|
||||
define void @test1_claimRV_autoreleaseRV() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
entry:
|
||||
%call = invoke "clang.arc.rv"="claim" i8* @callee0_autoreleaseRV()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
invoke.cont:
|
||||
ret void
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 }
|
||||
cleanup
|
||||
resume { i8*, i32 } undef
|
||||
}
|
||||
|
||||
define i8* @callee1_no_autoreleaseRV() {
|
||||
%call = call i8* @foo0()
|
||||
ret i8* %call
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test2_no_autoreleaseRV(
|
||||
; CHECK: call "clang.arc.rv"="retain" i8* @foo0()
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test2_no_autoreleaseRV() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @callee1_no_autoreleaseRV()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test2_claimRV_no_autoreleaseRV(
|
||||
; CHECK: call "clang.arc.rv"="claim" i8* @foo0()
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test2_claimRV_no_autoreleaseRV() {
|
||||
%call = call "clang.arc.rv"="claim" i8* @callee1_no_autoreleaseRV()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test3_no_autoreleaseRV(
|
||||
; CHECK: invoke "clang.arc.rv"="retain" i8* @foo0()
|
||||
|
||||
define void @test3_no_autoreleaseRV() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
entry:
|
||||
%call = invoke "clang.arc.rv"="retain" i8* @callee1_no_autoreleaseRV()
|
||||
to label %invoke.cont unwind label %lpad
|
||||
|
||||
invoke.cont:
|
||||
ret void
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 }
|
||||
cleanup
|
||||
resume { i8*, i32 } undef
|
||||
}
|
||||
|
||||
define i8* @callee2_nocall() {
|
||||
%1 = load i8*, i8** @g0, align 8
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
; Check that a call to @llvm.objc.retain is inserted if there is no matching
|
||||
; autoreleaseRV call or a call.
|
||||
|
||||
; CHECK-LABEL: define void @test4_nocall(
|
||||
; CHECK: %[[V0:.*]] = load i8*, i8** @g0,
|
||||
; CHECK-NEXT: call i8* @llvm.objc.retain(i8* %[[V0]])
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test4_nocall() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @callee2_nocall()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test4_claimRV_nocall(
|
||||
; CHECK: %[[V0:.*]] = load i8*, i8** @g0,
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test4_claimRV_nocall() {
|
||||
%call = call "clang.arc.rv"="claim" i8* @callee2_nocall()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Check that a call to @llvm.objc.retain is inserted if call to @foo already has
|
||||
; the attribute. I'm not sure this will happen in practice.
|
||||
|
||||
define i8* @callee3_marker() {
|
||||
%1 = call "clang.arc.rv"="retain" i8* @foo0()
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test5(
|
||||
; CHECK: %[[V0:.*]] = call "clang.arc.rv"="retain" i8* @foo0()
|
||||
; CHECK-NEXT: call i8* @llvm.objc.retain(i8* %[[V0]])
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test5() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @callee3_marker()
|
||||
ret void
|
||||
}
|
||||
|
||||
; Don't pair up an autoreleaseRV in the callee and an retainRV in the caller
|
||||
; if there is an instruction between the ret instruction and the call to
|
||||
; autoreleaseRV that isn't a cast instruction.
|
||||
|
||||
define i8* @callee0_autoreleaseRV2() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @foo0()
|
||||
%1 = tail call i8* @llvm.objc.autoreleaseReturnValue(i8* %call)
|
||||
store i8* null, i8** @g0
|
||||
ret i8* %call
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test6(
|
||||
; CHECK: %[[V0:.*]] = call "clang.arc.rv"="retain" i8* @foo0()
|
||||
; CHECK: call i8* @llvm.objc.autoreleaseReturnValue(i8* %[[V0]])
|
||||
; CHECK: store i8* null, i8** @g0, align 8
|
||||
; CHECK: call i8* @llvm.objc.retain(i8* %[[V0]])
|
||||
; CHECK-NEXT: ret void
|
||||
|
||||
define void @test6() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @callee0_autoreleaseRV2()
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i8* @llvm.objc.autoreleaseReturnValue(i8*)
|
||||
declare i32 @__gxx_personality_v0(...)
|
|
@ -10,16 +10,6 @@
|
|||
; }
|
||||
; }
|
||||
|
||||
; CHECK-LABEL: define void @"\01?g@@YAXXZ"()
|
||||
; CHECK-LABEL: catch
|
||||
; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [ "funclet"(token %1) ]
|
||||
|
||||
; CHECK-LABEL: catch.1
|
||||
; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [ "funclet"(token %1) ]
|
||||
|
||||
; CHECK-LABEL: invoke.cont
|
||||
; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""(){{$}}
|
||||
|
||||
define void @"\01?g@@YAXXZ"() personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
|
||||
entry:
|
||||
%call = invoke i8* @"\01?f@@YAPAUobjc_object@@XZ"()
|
||||
|
@ -50,41 +40,23 @@ invoke.cont: ; preds = %entry
|
|||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define dso_local void @"?test_attr_claimRV@@YAXXZ"()
|
||||
; CHECK: %[[CALL4:.*]] = notail call "clang.arc.rv"="claim" i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"()
|
||||
; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8* %[[CALL4]])
|
||||
|
||||
; CHECK: %[[V1:.*]] = cleanuppad
|
||||
; CHECK: %[[CALL:.*]] = notail call "clang.arc.rv"="claim" i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() [ "funclet"(token %[[V1]]) ]
|
||||
; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8* %[[CALL]]) [ "funclet"(token %[[V1]]) ]
|
||||
|
||||
define dso_local void @"?test_attr_claimRV@@YAXXZ"() local_unnamed_addr #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) {
|
||||
entry:
|
||||
invoke void @"?foo@@YAXXZ"()
|
||||
to label %invoke.cont unwind label %ehcleanup
|
||||
|
||||
invoke.cont: ; preds = %entry
|
||||
%call.i4 = tail call "clang.arc.rv"="claim" i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() #2
|
||||
ret void
|
||||
|
||||
ehcleanup: ; preds = %entry
|
||||
%0 = cleanuppad within none []
|
||||
%call.i = call "clang.arc.rv"="claim" i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"() #2 [ "funclet"(token %0) ]
|
||||
cleanupret from %0 unwind to caller
|
||||
}
|
||||
|
||||
declare i8* @"\01?f@@YAPAUobjc_object@@XZ"()
|
||||
|
||||
declare i32 @__CxxFrameHandler3(...)
|
||||
|
||||
declare void @"?foo@@YAXXZ"()
|
||||
declare i8* @"?noexcept_func@@YAPAUobjc_object@@XZ"()
|
||||
|
||||
declare dllimport i8* @llvm.objc.retainAutoreleasedReturnValue(i8*)
|
||||
declare i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8*)
|
||||
|
||||
declare dllimport void @llvm.objc.release(i8*)
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
|
||||
!0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker", !"movl\09%ebp, %ebp\09\09// marker for objc_retainAutoreleaseReturnValue"}
|
||||
|
||||
; CHECK-LABEL: catch
|
||||
; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [ "funclet"(token %1) ]
|
||||
|
||||
; CHECK-LABEL: catch.1
|
||||
; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""() [ "funclet"(token %1) ]
|
||||
|
||||
; CHECK-LABEL: invoke.cont
|
||||
; CHECK: call void asm sideeffect "movl{{.*}}%ebp, %ebp{{.*}}", ""(){{$}}
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
; RUN: opt -objc-arc-contract -S < %s | FileCheck %s
|
||||
; RUN: opt -passes=objc-arc-contract -S < %s | FileCheck %s
|
||||
|
||||
; CHECK-LABEL: define void @test0() {
|
||||
; CHECK: %[[CALL:.*]] = notail call "clang.arc.rv"="retain" i8* @foo()
|
||||
; CHECK: call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL]])
|
||||
|
||||
define void @test0() {
|
||||
%call1 = call "clang.arc.rv"="retain" i8* @foo()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @test1() {
|
||||
; CHECK: %[[CALL:.*]] = notail call "clang.arc.rv"="claim" i8* @foo()
|
||||
; CHECK: call i8* @llvm.objc.unsafeClaimAutoreleasedReturnValue(i8* %[[CALL]])
|
||||
|
||||
define void @test1() {
|
||||
%call1 = call "clang.arc.rv"="claim" i8* @foo()
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL:define i8* @test2(
|
||||
; CHECK: %[[CALL1:.*]] = invoke "clang.arc.rv"="retain" i8* @foo()
|
||||
|
||||
; CHECK: %[[V0:.*]] = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL1]])
|
||||
; CHECK-NEXT: br
|
||||
|
||||
; CHECK: %[[CALL3:.*]] = invoke "clang.arc.rv"="retain" i8* @foo()
|
||||
|
||||
; CHECK: %[[V2:.*]] = call i8* @llvm.objc.retainAutoreleasedReturnValue(i8* %[[CALL3]])
|
||||
; CHECK-NEXT: br
|
||||
|
||||
; CHECK: %[[RETVAL:.*]] = phi i8* [ %[[V0]], {{.*}} ], [ %[[V2]], {{.*}} ]
|
||||
; CHECK: ret i8* %[[RETVAL]]
|
||||
|
||||
define i8* @test2(i1 zeroext %b) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
|
||||
entry:
|
||||
br i1 %b, label %if.then, label %if.end
|
||||
|
||||
if.then:
|
||||
%call1 = invoke "clang.arc.rv"="retain" i8* @foo()
|
||||
to label %cleanup unwind label %lpad
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 }
|
||||
cleanup
|
||||
resume { i8*, i32 } undef
|
||||
|
||||
if.end:
|
||||
%call3 = invoke "clang.arc.rv"="retain" i8* @foo()
|
||||
to label %cleanup unwind label %lpad
|
||||
|
||||
cleanup:
|
||||
%retval.0 = phi i8* [ %call1, %if.then ], [ %call3, %if.end ]
|
||||
ret i8* %retval.0
|
||||
}
|
||||
|
||||
declare i8* @foo()
|
||||
declare i32 @__gxx_personality_v0(...)
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
|
||||
!0 = !{i32 1, !"clang.arc.retainAutoreleasedReturnValueMarker", !"mov\09fp, fp\09\09// marker for objc_retainAutoreleaseReturnValue"}
|
|
@ -452,28 +452,6 @@ bb1:
|
|||
ret i8* %v3
|
||||
}
|
||||
|
||||
; Remove attributes and the autoreleaseRV call if the call is a tail call.
|
||||
|
||||
; CHECK-LABEL: define i8* @test31(
|
||||
; CHECK: %[[CALL:.*]] = tail call i8* @returner()
|
||||
; CHECK: ret i8* %[[CALL]]
|
||||
|
||||
define i8* @test31() {
|
||||
%call = tail call "clang.arc.rv"="retain" i8* @returner()
|
||||
%1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %call)
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define i8* @test32(
|
||||
; CHECK: %[[CALL:.*]] = call "clang.arc.rv"="retain" i8* @returner()
|
||||
; CHECK: call i8* @llvm.objc.autoreleaseReturnValue(i8* %[[CALL]])
|
||||
|
||||
define i8* @test32() {
|
||||
%call = call "clang.arc.rv"="retain" i8* @returner()
|
||||
%1 = call i8* @llvm.objc.autoreleaseReturnValue(i8* %call)
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
!0 = !{}
|
||||
|
||||
; CHECK: attributes [[NUW]] = { nounwind }
|
||||
|
|
Loading…
Reference in New Issue