From eecad07d0060f4554f0b5839985b64b3305a4683 Mon Sep 17 00:00:00 2001 From: Sanjoy Das Date: Thu, 26 Nov 2015 01:16:05 +0000 Subject: [PATCH] [OperandBundles] Treat "deopt" operand bundles specially Teach LLVM optimize to more precisely in the presence of "deopt" operand bundles. "deopt" operand bundles imply that the call they're attached to is at least `readonly` (i.e. they don't imply clobber semantics), and they don't capture their bundle operands. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@254118 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/IR/InstrTypes.h | 23 ++++++++++--- test/Feature/OperandBundles/adce.ll | 8 +++++ .../OperandBundles/basic-aa-argmemonly.ll | 23 +++++++++++++ test/Feature/OperandBundles/dse.ll | 33 +++++++++++++++++++ test/Feature/OperandBundles/early-cse.ll | 22 +++++++++++-- test/Feature/OperandBundles/function-attrs.ll | 9 +++++ 6 files changed, 112 insertions(+), 6 deletions(-) diff --git a/include/llvm/IR/InstrTypes.h b/include/llvm/IR/InstrTypes.h index 32dceeac3fc..81de6999cdb 100644 --- a/include/llvm/IR/InstrTypes.h +++ b/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 *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/test/Feature/OperandBundles/adce.ll b/test/Feature/OperandBundles/adce.ll index 33bb87e5fc1..a729ba71068 100644 --- a/test/Feature/OperandBundles/adce.ll +++ b/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/test/Feature/OperandBundles/basic-aa-argmemonly.ll b/test/Feature/OperandBundles/basic-aa-argmemonly.ll index 1f3b378868e..aa944588606 100644 --- a/test/Feature/OperandBundles/basic-aa-argmemonly.ll +++ b/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/test/Feature/OperandBundles/dse.ll b/test/Feature/OperandBundles/dse.ll index cc5647a2fa0..9ddf7f02e38 100644 --- a/test/Feature/OperandBundles/dse.ll +++ b/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/test/Feature/OperandBundles/early-cse.ll b/test/Feature/OperandBundles/early-cse.ll index 076ce3baeab..fc201479d8c 100644 --- a/test/Feature/OperandBundles/early-cse.ll +++ b/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/test/Feature/OperandBundles/function-attrs.ll b/test/Feature/OperandBundles/function-attrs.ll index 3d429b3e9d9..808f396fed8 100644 --- a/test/Feature/OperandBundles/function-attrs.ll +++ b/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 +} -- 2.34.1