Index: include/llvm/IR/CallSite.h =================================================================== --- include/llvm/IR/CallSite.h +++ include/llvm/IR/CallSite.h @@ -333,6 +333,14 @@ CALLSITE_DELEGATE_GETTER(getOperandBundle(Index)); } + bool isOperandBundleUse(const Use &U) const { + CALLSITE_DELEGATE_GETTER(isOperandBundleUse(U)); + } + + bool isCapturingOperandBundleUse(const Use &U) const { + CALLSITE_DELEGATE_GETTER(isCapturingOperandBundleUse(U)); + } + #undef CALLSITE_DELEGATE_GETTER #undef CALLSITE_DELEGATE_SETTER Index: include/llvm/IR/InstrTypes.h =================================================================== --- include/llvm/IR/InstrTypes.h +++ include/llvm/IR/InstrTypes.h @@ -17,6 +17,7 @@ #define LLVM_IR_INSTRTYPES_H #include "llvm/ADT/Twine.h" +#include "llvm/ADT/Optional.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/OperandTraits.h" @@ -1197,21 +1198,50 @@ /// \brief Return the operand bundle at a specific index. OperandBundleUse getOperandBundle(unsigned Index) const { assert(Index < getNumOperandBundles() && "Index out of bounds!"); - auto *BOI = bundle_op_info_begin() + Index; - auto op_begin = static_cast(this)->op_begin(); - ArrayRef Inputs(op_begin + BOI->Begin, op_begin + BOI->End); - return OperandBundleUse(BOI->Tag->getKey(), Inputs); + return bundleOpInfoToOpBundleUse(bundle_op_info_begin()[Index]); } /// \brief Return the operand bundle at a specific index. OperandBundleUse getOperandBundle(unsigned Index) { assert(Index < getNumOperandBundles() && "Index out of bounds!"); - auto *BOI = bundle_op_info_begin() + Index; - auto op_begin = static_cast(this)->op_begin(); - ArrayRef Inputs(op_begin + BOI->Begin, op_begin + BOI->End); - return OperandBundleUse(BOI->Tag->getKey(), Inputs); + return bundleOpInfoToOpBundleUse(bundle_op_info_begin()[Index]); + } + + /// \brief Return the operand bundle containing U, and None if U is not + /// contained in an operand bundle. + Optional getContainingOperandBundle(const Use &U) const { + if (!hasOperandBundles()) + return None; + + unsigned UseIndex = &U - static_cast(this)->op_begin(); + assert(UseIndex < static_cast(this)->getNumOperands() && + "U not a Use for this User!"); + + for (auto &BOI : bundle_op_infos()) + if (UseIndex <= BOI.Begin && UseIndex < BOI.End) + return bundleOpInfoToOpBundleUse(BOI); + + return None; + } + + /// \brief Return true if the use U is an operand bundle use. + bool isOperandBundleUse(const Use &U) const { + return getContainingOperandBundle(U).hasValue(); + } + + /// \brief Return true if U captured by via an operand bundle. + bool isCapturingOperandBundleUse(const Use &U) const { + return isOperandBundleUse(U); } + /// \brief Return true if this operand bundle user has operand bundles that + /// may read from the heap. + bool hasReadingOperandBundles() const { return hasOperandBundles(); } + + /// \brief Return true if this operand bundle user has operand bundles that + /// may write to the heap. + bool hasClobberingOperandBundles() const { return hasOperandBundles(); } + protected: /// \brief Used to keep track of an operand bundle. See the main comment on /// OperandBundleUser above. @@ -1229,6 +1259,13 @@ uint32_t End; }; + /// \brief Helper function create an OperandBundleUse out of an BundleOpInfo. + OperandBundleUse bundleOpInfoToOpBundleUse(const BundleOpInfo &BOI) const { + auto op_begin = static_cast(this)->op_begin(); + ArrayRef Inputs(op_begin + BOI.Begin, op_begin + BOI.End); + return OperandBundleUse(BOI.Tag->getKey(), Inputs); + } + typedef BundleOpInfo *bundle_op_iterator; typedef const BundleOpInfo *const_bundle_op_iterator; Index: include/llvm/IR/Instructions.h =================================================================== --- include/llvm/IR/Instructions.h +++ include/llvm/IR/Instructions.h @@ -1739,9 +1739,21 @@ } private: + bool isFnAttrDisallowedByOpBundle(StringRef S) const { return false; } + + bool isFnAttrDisallowedByOpBundle(Attribute::AttrKind A) const { + return ((hasReadingOperandBundles() && A == Attribute::ReadNone) || + (hasClobberingOperandBundles() && + (A == Attribute::ReadOnly || A == Attribute::ReadNone))); + } + template bool hasFnAttrImpl(AttrKind A) const { if (AttributeList.hasAttribute(AttributeSet::FunctionIndex, A)) return true; + + if (isFnAttrDisallowedByOpBundle(A)) + return false; + if (const Function *F = getCalledFunction()) return F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, A); return false; Index: lib/Analysis/CaptureTracking.cpp =================================================================== --- lib/Analysis/CaptureTracking.cpp +++ lib/Analysis/CaptureTracking.cpp @@ -240,6 +240,13 @@ case Instruction::Call: case Instruction::Invoke: { CallSite CS(I); + + if (CS.isCapturingOperandBundleUse(*U)) { + if (Tracker->captured(U)) + return; + break; + } + // Not captured if the callee is readonly, doesn't return a copy through // its return value and doesn't unwind (a readonly function can leak bits // by throwing an exception or not depending on the input value). Index: lib/IR/Instructions.cpp =================================================================== --- lib/IR/Instructions.cpp +++ lib/IR/Instructions.cpp @@ -563,6 +563,12 @@ bool InvokeInst::hasFnAttrImpl(Attribute::AttrKind A) const { if (AttributeList.hasAttribute(AttributeSet::FunctionIndex, A)) return true; + + if ((hasReadingOperandBundles() && A == Attribute::ReadNone) || + (hasClobberingOperandBundles() && + (A == Attribute::ReadOnly || A == Attribute::ReadNone))) + return false; + if (const Function *F = getCalledFunction()) return F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, A); return false; Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -125,7 +125,10 @@ // Some instructions can be ignored even if they read or write memory. // Detect these now, skipping to the next instruction if one is found. CallSite CS(cast(I)); - if (CS) { + + // If the call or invoke has operand bundles, we let the generic + // mayWriteToMemory / mayReadFromMemory based handling kick in. + if (CS && !CS.hasOperandBundles()) { // Ignore calls to functions in the same SCC. if (CS.getCalledFunction() && SCCNodes.count(CS.getCalledFunction())) continue; @@ -429,8 +432,9 @@ case Instruction::Call: case Instruction::Invoke: { bool Captures = true; + CallSite CS(I); - if (I->getType()->isVoidTy()) + if (I->getType()->isVoidTy() && !CS.isCapturingOperandBundleUse(*U)) Captures = false; auto AddUsersToWorklistIfCapturing = [&] { @@ -440,14 +444,13 @@ Worklist.push_back(&UU); }; - CallSite CS(I); if (CS.doesNotAccessMemory()) { AddUsersToWorklistIfCapturing(); continue; } Function *F = CS.getCalledFunction(); - if (!F) { + if (!F || CS.isOperandBundleUse(*U)) { if (CS.onlyReadsMemory()) { IsRead = true; AddUsersToWorklistIfCapturing(); Index: test/Transforms/FunctionAttrs/operand-bundles.ll =================================================================== --- /dev/null +++ test/Transforms/FunctionAttrs/operand-bundles.ll @@ -0,0 +1,12 @@ +; RUN: opt -functionattrs -S < %s | FileCheck %s + +declare void @readnone_callee() readnone + +; %ptr should not be marked readnone or nocapture. + +; CHECK: define void @f(i32* %ptr) { +define void @f(i32* %ptr) { + entry: + call void @readnone_callee() [ "foo"(i32* %ptr) ] + ret void +} Index: test/Transforms/PruneEH/operand-bundles.ll =================================================================== --- /dev/null +++ test/Transforms/PruneEH/operand-bundles.ll @@ -0,0 +1,12 @@ +; RUN: opt -prune-eh -S < %s | FileCheck %s + +declare void @readnone_callee() nounwind + +; CHECK: define void @f(i32* %ptr) #0 { +define void @f(i32* %ptr) { + entry: + call void @readnone_callee() [ "foo"(i32* %ptr) ] + ret void +} + +; CHECK: attributes #0 = { nounwind }