Index: llvm/include/llvm/IR/InstrTypes.h =================================================================== --- llvm/include/llvm/IR/InstrTypes.h +++ llvm/include/llvm/IR/InstrTypes.h @@ -1818,14 +1818,14 @@ /// Determine if the call does not access or only reads memory. bool onlyReadsMemory() const { - return doesNotAccessMemory() || hasFnAttr(Attribute::ReadOnly); + return hasImpliedFnAttr(Attribute::ReadOnly); } void setOnlyReadsMemory() { addFnAttr(Attribute::ReadOnly); } /// Determine if the call does not access or only writes memory. bool onlyWritesMemory() const { - return doesNotAccessMemory() || hasFnAttr(Attribute::WriteOnly); + return hasImpliedFnAttr(Attribute::WriteOnly); } void setOnlyWritesMemory() { addFnAttr(Attribute::WriteOnly); } @@ -2113,6 +2113,9 @@ case Attribute::ReadOnly: return hasClobberingOperandBundles(); + + case Attribute::WriteOnly: + return hasReadingOperandBundles(); } llvm_unreachable("switch has a default case!"); @@ -2285,6 +2288,26 @@ return hasFnAttrOnCalledFunction(Kind); } + // A specialized version of hasFnAttrImpl for when the caller wants to + // know if an attribute's semantics are implied, not whether the attribute + // is actually present. This distinction only exists when checking whether + // something is readonly or writeonly since readnone implies both. The case + // which motivates the specialized code is a callee with readnone, and an + // operand bundle on the call which disallows readnone but not either + // readonly or writeonly. + bool hasImpliedFnAttr(Attribute::AttrKind Kind) const { + assert((Kind == Attribute::ReadOnly || Kind == Attribute::WriteOnly) && + "use hasFnAttrImpl instead"); + if (Attrs.hasFnAttr(Kind) || Attrs.hasFnAttr(Attribute::ReadNone)) + return true; + + if (isFnAttrDisallowedByOpBundle(Kind)) + return false; + + return hasFnAttrOnCalledFunction(Kind) || + hasFnAttrOnCalledFunction(Attribute::ReadNone); + } + /// Determine whether the return value has the given attribute. Supports /// Attribute::AttrKind and StringRef as \p AttrKind types. template bool hasRetAttrImpl(AttrKind Kind) const { Index: llvm/test/Transforms/InstCombine/trivial-dse-calls.ll =================================================================== --- llvm/test/Transforms/InstCombine/trivial-dse-calls.ll +++ llvm/test/Transforms/InstCombine/trivial-dse-calls.ll @@ -253,7 +253,6 @@ define void @test_readnone_with_deopt() { ; CHECK-LABEL: @test_readnone_with_deopt( -; CHECK-NEXT: call void @removable_readnone() [ "deopt"() ] ; CHECK-NEXT: ret void ; call void @removable_readnone() [ "deopt"() ]