[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
This commit is contained in:
Sanjoy Das 2015-11-26 01:16:05 +00:00
parent aa23e0330d
commit eecad07d00
6 changed files with 112 additions and 6 deletions

View File

@ -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:

View File

@ -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
}

View File

@ -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
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
}