Skip to content

[ObjC][ARC] Use the addresses of the ARC runtime functions instead of integer 0/1 for the operand of bundle "clang.arc.attachedcall" #3518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 34 additions & 46 deletions clang/lib/CodeGen/CGObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2108,6 +2108,13 @@ static void setARCRuntimeFunctionLinkage(CodeGenModule &CGM,
setARCRuntimeFunctionLinkage(CGM, RTF.getCallee());
}

static llvm::Function *getARCIntrinsic(llvm::Intrinsic::ID IntID,
CodeGenModule &CGM) {
llvm::Function *fn = CGM.getIntrinsic(IntID);
setARCRuntimeFunctionLinkage(CGM, fn);
return fn;
}

/// Perform an operation having the signature
/// i8* (i8*)
/// where a null input causes a no-op and returns null.
Expand All @@ -2118,10 +2125,8 @@ static llvm::Value *emitARCValueOperation(
if (isa<llvm::ConstantPointerNull>(value))
return value;

if (!fn) {
fn = CGF.CGM.getIntrinsic(IntID);
setARCRuntimeFunctionLinkage(CGF.CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(IntID, CGF.CGM);

// Cast the argument to 'id'.
llvm::Type *origType = returnType ? returnType : value->getType();
Expand All @@ -2140,10 +2145,8 @@ static llvm::Value *emitARCValueOperation(
static llvm::Value *emitARCLoadOperation(CodeGenFunction &CGF, Address addr,
llvm::Function *&fn,
llvm::Intrinsic::ID IntID) {
if (!fn) {
fn = CGF.CGM.getIntrinsic(IntID);
setARCRuntimeFunctionLinkage(CGF.CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(IntID, CGF.CGM);

// Cast the argument to 'id*'.
llvm::Type *origType = addr.getElementType();
Expand All @@ -2168,10 +2171,8 @@ static llvm::Value *emitARCStoreOperation(CodeGenFunction &CGF, Address addr,
bool ignored) {
assert(addr.getElementType() == value->getType());

if (!fn) {
fn = CGF.CGM.getIntrinsic(IntID);
setARCRuntimeFunctionLinkage(CGF.CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(IntID, CGF.CGM);

llvm::Type *origType = value->getType();

Expand All @@ -2193,10 +2194,8 @@ static void emitARCCopyOperation(CodeGenFunction &CGF, Address dst, Address src,
llvm::Intrinsic::ID IntID) {
assert(dst.getType() == src.getType());

if (!fn) {
fn = CGF.CGM.getIntrinsic(IntID);
setARCRuntimeFunctionLinkage(CGF.CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(IntID, CGF.CGM);

llvm::Value *args[] = {
CGF.Builder.CreateBitCast(dst.getPointer(), CGF.Int8PtrPtrTy),
Expand Down Expand Up @@ -2340,13 +2339,19 @@ static llvm::Value *emitOptimizedARCReturnCall(llvm::Value *value,
// 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 operand bundle.
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;
EP = getARCIntrinsic(IID, CGF.CGM);

// FIXME: Do this when the target isn't aarch64.
if (CGF.CGM.getCodeGenOpts().OptimizationLevel > 0 &&
CGF.CGM.getTarget().getTriple().isAArch64()) {
llvm::Value *bundleArgs[] = {llvm::ConstantInt::get(
CGF.Int64Ty,
llvm::objcarc::getAttachedCallOperandBundleEnum(IsRetainRV))};
llvm::Value *bundleArgs[] = {EP};
llvm::OperandBundleDef OB("clang.arc.attachedcall", bundleArgs);
auto *oldCall = cast<llvm::CallBase>(value);
llvm::CallBase *newCall = llvm::CallBase::addOperandBundle(
Expand All @@ -2362,13 +2367,6 @@ static llvm::Value *emitOptimizedARCReturnCall(llvm::Value *value,
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);
}

Expand Down Expand Up @@ -2401,10 +2399,8 @@ void CodeGenFunction::EmitARCRelease(llvm::Value *value,
if (isa<llvm::ConstantPointerNull>(value)) return;

llvm::Function *&fn = CGM.getObjCEntrypoints().objc_release;
if (!fn) {
fn = CGM.getIntrinsic(llvm::Intrinsic::objc_release);
setARCRuntimeFunctionLinkage(CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(llvm::Intrinsic::objc_release, CGM);

// Cast the argument to 'id'.
value = Builder.CreateBitCast(value, Int8PtrTy);
Expand Down Expand Up @@ -2447,10 +2443,8 @@ llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(Address addr,
assert(addr.getElementType() == value->getType());

llvm::Function *&fn = CGM.getObjCEntrypoints().objc_storeStrong;
if (!fn) {
fn = CGM.getIntrinsic(llvm::Intrinsic::objc_storeStrong);
setARCRuntimeFunctionLinkage(CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(llvm::Intrinsic::objc_storeStrong, CGM);

llvm::Value *args[] = {
Builder.CreateBitCast(addr.getPointer(), Int8PtrPtrTy),
Expand Down Expand Up @@ -2603,10 +2597,8 @@ void CodeGenFunction::EmitARCInitWeak(Address addr, llvm::Value *value) {
/// Essentially objc_storeWeak(addr, nil).
void CodeGenFunction::EmitARCDestroyWeak(Address addr) {
llvm::Function *&fn = CGM.getObjCEntrypoints().objc_destroyWeak;
if (!fn) {
fn = CGM.getIntrinsic(llvm::Intrinsic::objc_destroyWeak);
setARCRuntimeFunctionLinkage(CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(llvm::Intrinsic::objc_destroyWeak, CGM);

// Cast the argument to 'id*'.
addr = Builder.CreateBitCast(addr, Int8PtrPtrTy);
Expand Down Expand Up @@ -2651,10 +2643,8 @@ void CodeGenFunction::emitARCMoveAssignWeak(QualType Ty, Address DstAddr,
/// call i8* \@objc_autoreleasePoolPush(void)
llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() {
llvm::Function *&fn = CGM.getObjCEntrypoints().objc_autoreleasePoolPush;
if (!fn) {
fn = CGM.getIntrinsic(llvm::Intrinsic::objc_autoreleasePoolPush);
setARCRuntimeFunctionLinkage(CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(llvm::Intrinsic::objc_autoreleasePoolPush, CGM);

return EmitNounwindRuntimeCall(fn);
}
Expand All @@ -2679,10 +2669,8 @@ void CodeGenFunction::EmitObjCAutoreleasePoolPop(llvm::Value *value) {
EmitRuntimeCallOrInvoke(fn, value);
} else {
llvm::FunctionCallee &fn = CGM.getObjCEntrypoints().objc_autoreleasePoolPop;
if (!fn) {
fn = CGM.getIntrinsic(llvm::Intrinsic::objc_autoreleasePoolPop);
setARCRuntimeFunctionLinkage(CGM, fn);
}
if (!fn)
fn = getARCIntrinsic(llvm::Intrinsic::objc_autoreleasePoolPop, CGM);

EmitRuntimeCall(fn, value);
}
Expand Down
20 changes: 10 additions & 10 deletions clang/test/CodeGenObjC/arc-rv-attr.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void test_assign() {
}
// CHECK-LABEL: define{{.*}} void @test_assign()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A:.*]]* @makeA() [ "clang.arc.attachedcall"(i64 1) ]
// CHECK: [[T0:%.*]] = call [[A:.*]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
Expand All @@ -25,7 +25,7 @@ void test_assign_assign() {
// CHECK-LABEL: define{{.*}} void @test_assign_assign()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[Y:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 1) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
Expand All @@ -44,7 +44,7 @@ void test_strong_assign_assign() {
// CHECK-LABEL: define{{.*}} void @test_strong_assign_assign()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[Y:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 0) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: store i8* [[T1]], i8** [[Y]]
Expand All @@ -67,7 +67,7 @@ void test_assign_strong_assign() {
// CHECK-LABEL: define{{.*}} void @test_assign_strong_assign()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[Y:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 0) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[Y]]
Expand All @@ -87,7 +87,7 @@ void test_init() {
}
// CHECK-LABEL: define{{.*}} void @test_init()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 1) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
Expand All @@ -102,7 +102,7 @@ void test_init_assignment() {
// CHECK-LABEL: define{{.*}} void @test_init_assignment()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[Y:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 1) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
Expand All @@ -120,7 +120,7 @@ void test_strong_init_assignment() {
// CHECK-LABEL: define{{.*}} void @test_strong_init_assignment()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[Y:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 0) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: store i8* [[T1]], i8** [[X]]
Expand All @@ -140,7 +140,7 @@ void test_init_strong_assignment() {
// CHECK-LABEL: define{{.*}} void @test_init_strong_assignment()
// CHECK: [[X:%.*]] = alloca i8*
// CHECK: [[Y:%.*]] = alloca i8*
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 0) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8*
// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]]
Expand All @@ -159,15 +159,15 @@ void test_ignored() {
makeA();
}
// CHECK-LABEL: define{{.*}} void @test_ignored()
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 1) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: ret void

void test_cast_to_void() {
(void)makeA();
}
// CHECK-LABEL: define{{.*}} void @test_cast_to_void()
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i64 1) ]
// CHECK: [[T0:%.*]] = call [[A]]* @makeA() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.unsafeClaimAutoreleasedReturnValue) ]
// CHECK-NEXT: call void (...) @llvm.objc.clang.arc.noop.use({{.*}} [[T0]])
// CHECK-NEXT: ret void

Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenObjCXX/arc-rv-attr.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
id foo(void);

// CHECK-LABEL: define{{.*}} void @_Z14test_list_initv(
// CHECK: %[[CALL1:.*]] = call i8* @_Z3foov() [ "clang.arc.attachedcall"(i64 0) ]
// CHECK: %[[CALL1:.*]] = call i8* @_Z3foov() [ "clang.arc.attachedcall"(i8* (i8*)* @llvm.objc.retainAutoreleasedReturnValue) ]
// CHECK: call i8* @llvm.objc.retain(i8* %[[CALL1]])

void test_list_init() {
Expand Down
26 changes: 19 additions & 7 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2459,14 +2459,26 @@ for further details.
ObjC ARC Attached Call Operand Bundles
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A ``"clang.arc.attachedcall`` operand bundle on a call indicates the call is
A ``"clang.arc.attachedcall"`` operand bundle on a call indicates the call is
implicitly followed by a marker instruction and a call to an ObjC runtime
function that uses the result of the call. If the argument passed to the operand
bundle is 0, ``@objc_retainAutoreleasedReturnValue`` is called. If 1 is passed,
``@objc_unsafeClaimAutoreleasedReturnValue`` is called. The return value of a
call with this bundle is used by a call to ``@llvm.objc.clang.arc.noop.use``
unless the called function's return type is void, in which case the operand
bundle is ignored.
function that uses the result of the call. The operand bundle takes either the
pointer to the runtime function (``@objc_retainAutoreleasedReturnValue`` or
``@objc_unsafeClaimAutoreleasedReturnValue``) or no arguments. If the bundle
doesn't take any arguments, only the marker instruction has to be emitted after
the call; the runtime function calls don't have to be emitted since they already
have been emitted. The return value of a call with this bundle is used by a call
to ``@llvm.objc.clang.arc.noop.use`` unless the called function's return type is
void, in which case the operand bundle is ignored.

.. code-block:: llvm

; The marker instruction and a runtime function call are inserted after the call
; to @foo.
call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_retainAutoreleasedReturnValue) ]
call i8* @foo() [ "clang.arc.attachedcall"(i8* (i8*)* @objc_unsafeClaimAutoreleasedReturnValue) ]

; Only the marker instruction is inserted after the call to @foo.
call i8* @foo() [ "clang.arc.attachedcall"() ]

The operand bundle is needed to ensure the call is immediately followed by the
marker instruction or the ObjC runtime call in the final output.
Expand Down
41 changes: 27 additions & 14 deletions llvm/include/llvm/Analysis/ObjCARCUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#ifndef LLVM_IR_OBJCARCUTIL_H
#define LLVM_IR_OBJCARCUTIL_H

#include "llvm/Analysis/ObjCARCInstKind.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/LLVMContext.h"

Expand All @@ -24,13 +26,6 @@ inline const char *getRVMarkerModuleFlagStr() {
return "clang.arc.retainAutoreleasedReturnValueMarker";
}

enum AttachedCallOperandBundle : unsigned { RVOB_Retain, RVOB_Claim };

inline AttachedCallOperandBundle
getAttachedCallOperandBundleEnum(bool IsRetain) {
return IsRetain ? RVOB_Retain : RVOB_Claim;
}

inline bool hasAttachedCallOpBundle(const CallBase *CB) {
// Ignore the bundle if the return type is void. Global optimization passes
// can turn the called function's return type to void. That should happen only
Expand All @@ -43,14 +38,32 @@ inline bool hasAttachedCallOpBundle(const CallBase *CB) {
.hasValue();
}

inline bool hasAttachedCallOpBundle(const CallBase *CB, bool IsRetain) {
assert(hasAttachedCallOpBundle(CB) &&
"call doesn't have operand bundle clang_arc_attachedcall");
/// This function returns operand bundle clang_arc_attachedcall's argument,
/// which is the address of the ARC runtime function.
inline Optional<Function *> getAttachedARCFunction(const CallBase *CB) {
auto B = CB->getOperandBundle(LLVMContext::OB_clang_arc_attachedcall);
if (!B.hasValue())
return false;
return cast<ConstantInt>(B->Inputs[0])->getZExtValue() ==
getAttachedCallOperandBundleEnum(IsRetain);
if (!B.hasValue() || B->Inputs.size() == 0)
return None;

return cast<Function>(B->Inputs[0]);
}

/// Check whether the function is retainRV/claimRV.
inline bool isRetainOrClaimRV(ARCInstKind Kind) {
return Kind == ARCInstKind::RetainRV || Kind == ARCInstKind::ClaimRV;
}

/// This function returns the ARCInstKind of the function attached to operand
/// bundle clang_arc_attachedcall. It returns None if the call doesn't have the
/// operand bundle or the operand is null. Otherwise it returns either RetainRV
/// or ClaimRV.
inline ARCInstKind getAttachedARCFunctionKind(const CallBase *CB) {
Optional<Function *> Fn = getAttachedARCFunction(CB);
if (!Fn.hasValue())
return ARCInstKind::None;
auto FnClass = GetFunctionClass(*Fn);
assert(isRetainOrClaimRV(FnClass) && "unexpected ARC runtime function");
return FnClass;
}

} // end namespace objcarc
Expand Down
9 changes: 5 additions & 4 deletions llvm/include/llvm/IR/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -904,13 +904,14 @@ class Function : public GlobalObject, public ilist_node<Function> {
/// hasAddressTaken - returns true if there are any uses of this function
/// other than direct calls or invokes to it, or blockaddress expressions.
/// Optionally passes back an offending user for diagnostic purposes,
/// ignores callback uses, assume like pointer annotation calls, and
/// references in llvm.used and llvm.compiler.used variables.
///
/// ignores callback uses, assume like pointer annotation calls, references in
/// llvm.used and llvm.compiler.used variables, and operand bundle
/// "clang.arc.attachedcall".
bool hasAddressTaken(const User ** = nullptr,
bool IgnoreCallbackUses = false,
bool IgnoreAssumeLikeCalls = true,
bool IngoreLLVMUsed = false) const;
bool IngoreLLVMUsed = false,
bool IgnoreARCAttachedCall = false) const;

/// isDefTriviallyDead - Return true if it is trivially safe to remove
/// this function definition from the module (because it isn't externally
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1918,6 +1918,13 @@ class CallBase : public Instruction {
Idx < getBundleOperandsEndIndex();
}

/// Return true if the operand at index \p Idx is a bundle operand that has
/// tag ID \p ID.
bool isOperandBundleOfType(uint32_t ID, unsigned Idx) const {
return isBundleOperand(Idx) &&
getOperandBundleForOperand(Idx).getTagID() == ID;
}

/// Returns true if the use is a bundle operand.
bool isBundleOperand(const Use *U) const {
assert(this == U->getUser() &&
Expand Down
Loading