summaryrefslogtreecommitdiffstats
path: root/llvm
diff options
context:
space:
mode:
Diffstat (limited to 'llvm')
-rw-r--r--llvm/docs/LangRef.rst9
-rw-r--r--llvm/include/llvm/IR/InstrTypes.h43
-rw-r--r--llvm/include/llvm/IR/Instructions.h6
-rw-r--r--llvm/lib/IR/Instructions.cpp6
-rw-r--r--llvm/test/Feature/OperandBundles/adce.ll41
-rw-r--r--llvm/test/Feature/OperandBundles/early-cse.ll71
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
+}
OpenPOWER on IntegriCloud