diff options
-rw-r--r-- | llvm/include/llvm/IR/InstrTypes.h | 23 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/adce.ll | 8 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/basic-aa-argmemonly.ll | 23 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/dse.ll | 33 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/early-cse.ll | 22 | ||||
-rw-r--r-- | llvm/test/Feature/OperandBundles/function-attrs.ll | 9 |
6 files changed, 112 insertions, 6 deletions
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index 32dceeac3fc..81de6999cdb 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -21,6 +21,7 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instruction.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IR/OperandTraits.h" namespace llvm { @@ -1126,6 +1127,9 @@ struct OperandBundleUse { /// Currently there is no way to have attributes on operand bundles differ on /// a per operand granularity. bool operandsHaveAttr(Attribute::AttrKind A) const { + if (isDeoptOperandBundle()) + return A == Attribute::ReadOnly || A == Attribute::NoCapture; + // Conservative answer: no operands have any attributes. return false; }; @@ -1144,6 +1148,11 @@ struct OperandBundleUse { return Tag->getValue(); } + /// \brief Return true if this is a "deopt" operand bundle. + bool isDeoptOperandBundle() const { + return getTagID() == LLVMContext::OB_deopt; + } + private: /// \brief Pointer to an entry in LLVMContextImpl::getOrInsertBundleTag. StringMapEntry<uint32_t> *Tag; @@ -1361,10 +1370,16 @@ public: /// \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(); + for (auto &BOI : bundle_op_infos()) { + if (BOI.Tag->second == LLVMContext::OB_deopt) + continue; + + // This instruction has an operand bundle that is not a "deopt" operand + // bundle. Assume the worst. + return true; + } + + return false; } protected: diff --git a/llvm/test/Feature/OperandBundles/adce.ll b/llvm/test/Feature/OperandBundles/adce.ll index 33bb87e5fc1..a729ba71068 100644 --- a/llvm/test/Feature/OperandBundles/adce.ll +++ b/llvm/test/Feature/OperandBundles/adce.ll @@ -39,3 +39,11 @@ define void @test3() { call void @readnone_function() readnone [ "tag"() ] ret void } + +define void @test4() { +; CHECK-LABEL: @test4( + entry: +; CHECK-NOT: @readonly_function() + call void @readonly_function() [ "deopt"() ] + ret void +} diff --git a/llvm/test/Feature/OperandBundles/basic-aa-argmemonly.ll b/llvm/test/Feature/OperandBundles/basic-aa-argmemonly.ll index 1f3b378868e..aa944588606 100644 --- a/llvm/test/Feature/OperandBundles/basic-aa-argmemonly.ll +++ b/llvm/test/Feature/OperandBundles/basic-aa-argmemonly.ll @@ -26,3 +26,26 @@ define i32 @test1(i32* %P, i32* noalias %P2) { ret i32 %diff ; CHECK: ret i32 0 } + +define i32 @test2(i32* %P, i32* noalias %P2) { +; Note: in this test we //can// GVN %v1 and %v2 into one value in theory. Calls +; with deopt operand bundles are not argmemonly because they *read* the entire +; heap, but they don't write to any location in the heap if the callee does not +; deoptimize the caller. This fact, combined with the fact that +; @argmemonly_function is, well, an argmemonly function, can be used to conclude +; that %P is not written to at the callsite. However LLVM currently cannot +; describe the "does not write to non-args, and reads the entire heap" effect on +; a callsite. + +; CHECK-LABEL: @test2( + %v1 = load i32, i32* %P +; CHECK: %v1 = load i32, i32* %P + call void @argmemonly_function(i32* %P2) [ "deopt"() ] +; CHECK: call void @argmemonly_function( + %v2 = load i32, i32* %P +; CHECK: %v2 = load i32, i32* %P + %diff = sub i32 %v1, %v2 +; CHECK: %diff = sub i32 %v1, %v2 + ret i32 %diff +; CHECK: ret i32 %diff +} diff --git a/llvm/test/Feature/OperandBundles/dse.ll b/llvm/test/Feature/OperandBundles/dse.ll index cc5647a2fa0..9ddf7f02e38 100644 --- a/llvm/test/Feature/OperandBundles/dse.ll +++ b/llvm/test/Feature/OperandBundles/dse.ll @@ -27,3 +27,36 @@ define i8* @test_1() { ret i8* %m } + +define void @test_2() { +; Since the deopt operand bundle does not escape %m (see caveat below), it is +; legal to elide the final store that location. + +; CHECK-LABEL: @test_2( + %m = call i8* @malloc(i32 24) + tail call void @f() [ "deopt"(i8* %m) ] + store i8 -19, i8* %m + ret void + +; CHECK: tail call void @f() [ "deopt"(i8* %m) ] +; CHECK-NEXT ret void +} + +define i8* @test_3() { +; Since the deopt operand bundle does not escape %m (see caveat below), @f +; cannot observe the stores to %m + +; CHECK-LABEL: @test_3( + %m = call i8* @malloc(i32 24) + tail call void @f() [ "deopt"(i8* %m) ] + store i8 -19, i8* %m + tail call void @f() + store i8 101, i8* %m + ret i8* %m +} + + +; Caveat: technically, %m can only escape if the calling function is deoptimized +; at the call site (i.e. the call returns to the "deopt" continuation). Since +; the calling function body will be invalidated in that case, the calling +; function can be optimized under the assumption that %m does not escape. diff --git a/llvm/test/Feature/OperandBundles/early-cse.ll b/llvm/test/Feature/OperandBundles/early-cse.ll index 076ce3baeab..fc201479d8c 100644 --- a/llvm/test/Feature/OperandBundles/early-cse.ll +++ b/llvm/test/Feature/OperandBundles/early-cse.ll @@ -2,8 +2,8 @@ ; 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. +; they're carrying unknown 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 @@ -69,3 +69,21 @@ define void @test5(i32* %x) { ; CHECK: store i32 200, i32* %x ret void } + +define void @test6(i32* %x) { +; The "deopt" operand bundle does not make the call to +; @readonly_function read-write; and so the nounwind readonly call can +; be deleted. + +; CHECK-LABEL: @test6( + entry: + +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 200, i32* %x +; CHECK-NEXT: ret void + + store i32 100, i32* %x + call void @readonly_function() [ "deopt"() ] + store i32 200, i32* %x + ret void +} diff --git a/llvm/test/Feature/OperandBundles/function-attrs.ll b/llvm/test/Feature/OperandBundles/function-attrs.ll index 3d429b3e9d9..808f396fed8 100644 --- a/llvm/test/Feature/OperandBundles/function-attrs.ll +++ b/llvm/test/Feature/OperandBundles/function-attrs.ll @@ -22,3 +22,12 @@ define void @test_1(i32* %x) { call void @f_readnone() [ "foo"(i32* %x) ] ret void } + +define void @test_2(i32* %x) { +; The "deopt" operand bundle does not capture or write to %x. + +; CHECK-LABEL: define void @test_2(i32* nocapture readonly %x) + entry: + call void @f_readonly() [ "deopt"(i32* %x) ] + ret void +} |