diff options
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/docs/LangRef.rst | 9 | ||||
-rw-r--r-- | llvm/include/llvm/IR/InstrTypes.h | 43 | ||||
-rw-r--r-- | llvm/include/llvm/IR/Instructions.h | 6 | ||||
-rw-r--r-- | llvm/lib/IR/Instructions.cpp | 6 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/adce.ll | 41 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/early-cse.ll | 71 |
6 files changed, 176 insertions, 0 deletions
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 4368cca4b54..525b01c87ac 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1481,6 +1481,15 @@ operand bundle to not miscompile programs containing it. - An operand bundle at a call site cannot change the implementation of the called function. Inter-procedural optimizations work as usual as long as they take into account the first two properties. +- The bundle operands for an unknown operand bundle escape in unknown + ways before control is transferred to the callee or invokee. +- Calls and invokes with operand bundles have unknown read / write + effect on the heap on entry and exit (even if the call target is + ``readnone`` or ``readonly``), unless they're overriden with + callsite specific attributes. +- An operand bundle at a call site cannot change the implementation + of the called function. Inter-procedural optimizations work as + usual as long as they take into account the first two properties. .. _moduleasm: diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index c3bbe22069c..136a6c3f457 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -18,6 +18,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/Twine.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/OperandTraits.h" @@ -1232,8 +1233,50 @@ public: return None; } + /// \brief Return true if this operand bundle user has operand bundles that + /// may read from the heap. + bool hasReadingOperandBundles() const { + // Implementation note: this is a conservative implementation of operand + // bundle semantics, where *any* operand bundle forces a callsite to be at + // least readonly. + return hasOperandBundles(); + } + + /// \brief Return true if this operand bundle user has operand bundles that + /// may write to the heap. + bool hasClobberingOperandBundles() const { + // Implementation note: this is a conservative implementation of operand + // bundle semantics, where *any* operand bundle forces a callsite to be + // read-write. + return hasOperandBundles(); + } protected: + /// \brief Is the function attribute S disallowed by some operand bundle on + /// this operand bundle user? + bool isFnAttrDisallowedByOpBundle(StringRef S) const { + // Operand bundles only possibly disallow readnone and readonly attributes. + // All String attributes are fine. + return false; + } + + /// \brief Is the function attribute A disallowed by some operand bundle on + /// this operand bundle user? + bool isFnAttrDisallowedByOpBundle(Attribute::AttrKind A) const { + switch (A) { + default: + return false; + + case Attribute::ReadNone: + return hasReadingOperandBundles(); + + case Attribute::ReadOnly: + return hasClobberingOperandBundles(); + } + + llvm_unreachable("switch has a default case!"); + } + /// \brief Used to keep track of an operand bundle. See the main comment on /// OperandBundleUser above. struct BundleOpInfo { diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 9fb44ca07a6..cb3a260e0d5 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -1742,6 +1742,12 @@ private: template <typename AttrKind> bool hasFnAttrImpl(AttrKind A) const { if (AttributeList.hasAttribute(AttributeSet::FunctionIndex, A)) return true; + + // Operand bundles override attributes on the called function, but don't + // override attributes directly present on the call instruction. + if (isFnAttrDisallowedByOpBundle(A)) + return false; + if (const Function *F = getCalledFunction()) return F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, A); return false; diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index 49f9fe83708..855101b4ac8 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -563,6 +563,12 @@ void InvokeInst::setSuccessorV(unsigned idx, BasicBlock *B) { bool InvokeInst::hasFnAttrImpl(Attribute::AttrKind A) const { if (AttributeList.hasAttribute(AttributeSet::FunctionIndex, A)) return true; + + // Operand bundles override attributes on the called function, but don't + // override attributes directly present on the invoke instruction. + if (isFnAttrDisallowedByOpBundle(A)) + return false; + if (const Function *F = getCalledFunction()) return F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, A); return false; diff --git a/llvm/test/Feature/OperandBundles/adce.ll b/llvm/test/Feature/OperandBundles/adce.ll new file mode 100644 index 00000000000..33bb87e5fc1 --- /dev/null +++ b/llvm/test/Feature/OperandBundles/adce.ll @@ -0,0 +1,41 @@ +; RUN: opt -S -adce < %s | FileCheck %s + +; While it is normally okay to DCE out calls to @readonly_function and +; @readnone_function, we cannot do that if they're carrying operand +; bundles since the presence of unknown operand bundles implies +; arbitrary memory effects. + +declare void @readonly_function() readonly nounwind +declare void @readnone_function() readnone nounwind + +define void @test0() { +; CHECK-LABEL: @test0( + entry: + call void @readonly_function() [ "tag"() ] +; CHECK: call void @readonly_function + ret void +} + +define void @test1() { +; CHECK-LABEL: @test1( + entry: + call void @readnone_function() [ "tag"() ] +; CHECK: call void @readnone_function + ret void +} + +define void @test2() { +; CHECK-LABEL: @test2( + entry: +; CHECK-NOT: @readonly_function( + call void @readonly_function() readonly [ "tag"() ] + ret void +} + +define void @test3() { +; CHECK-LABEL: @test3( + entry: +; CHECK-NOT: @readnone_function( + call void @readnone_function() readnone [ "tag"() ] + ret void +} diff --git a/llvm/test/Feature/OperandBundles/early-cse.ll b/llvm/test/Feature/OperandBundles/early-cse.ll new file mode 100644 index 00000000000..076ce3baeab --- /dev/null +++ b/llvm/test/Feature/OperandBundles/early-cse.ll @@ -0,0 +1,71 @@ +; RUN: opt -S -early-cse < %s | FileCheck %s + +; While it is normally okay to do memory optimizations over calls to +; @readonly_function and @readnone_function, we cannot do that if +; they're carrying operand bundles since the presence of unknown +; operand bundles implies arbitrary memory effects. + +declare void @readonly_function() readonly nounwind +declare void @readnone_function() readnone nounwind + +define i32 @test0(i32* %x) { +; CHECK-LABEL: @test0( + entry: + store i32 100, i32* %x +; CHECK: store i32 100, i32* %x + call void @readonly_function() [ "tag"() ] +; CHECK: call void @readonly_function() + + %v = load i32, i32* %x +; CHECK: %v = load i32, i32* %x +; CHECK: ret i32 %v + ret i32 %v +} + +define i32 @test1(i32* %x) { +; CHECK: @test1( + entry: + store i32 100, i32* %x +; CHECK: store i32 100, i32* %x + call void @readonly_function() readonly [ "tag"() ] +; CHECK-NOT: call void @readonly_function + %v = load i32, i32* %x + ret i32 %v +; CHECK: ret i32 100 +} + +define i32 @test3(i32* %x) { +; CHECK-LABEL: @test3( + entry: + store i32 100, i32* %x +; CHECK: store i32 100, i32* %x + call void @readonly_function() +; CHECK-NOT: call void @readonly_function + %v = load i32, i32* %x + ret i32 %v +; CHECK: ret i32 100 +} + +define void @test4(i32* %x) { +; CHECK-LABEL: @test4( + entry: + store i32 100, i32* %x +; CHECK: store i32 100, i32* %x + call void @readnone_function() [ "tag"() ] +; CHECK: call void @readnone_function + store i32 200, i32* %x +; CHECK: store i32 200, i32* %x + ret void +} + +define void @test5(i32* %x) { +; CHECK-LABEL: @test5( + entry: + store i32 100, i32* %x +; CHECK-NOT: store i32 100, i32* %x +; CHECK-NOT: call void @readnone_function + call void @readnone_function() readnone [ "tag"() ] + store i32 200, i32* %x +; CHECK: store i32 200, i32* %x + ret void +} |