#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 {
/// 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;
};
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;
/// \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:
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
+}
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
+}
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.
; 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
; 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
+}
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
+}