forked from OSchip/llvm-project
[Statepoint Lowering] Handle the case with several gc.result
Recently gc.result has been marked with readnone instead of readonly and this opens a door for different optimization to duplicate gc.result. Statepoint lowering is not ready to see several gc.results. The problem appears when there are gc.results with one located in the same basic block and another located in other basic block. In this case we need both export VR and fill local setValue. Note that this case is not sufficient optimization done before CodeGen. It is evident that local gc.result dominates all other gc.results and it is handled by GVN and EarlyCSE. But anyway, even if IR is not optimal Backend should not crash on a valid IR. Reviewers: reames, dantrushin Reviewed By: dantrushin Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D98393
This commit is contained in:
parent
2aef202981
commit
0480927712
|
@ -203,14 +203,10 @@ public:
|
|||
/// path of invoke.
|
||||
inline std::vector<const GCRelocateInst *> getGCRelocates() const;
|
||||
|
||||
/// Get the experimental_gc_result call tied to this statepoint if there is
|
||||
/// one, otherwise return nullptr.
|
||||
const GCResultInst *getGCResult() const {
|
||||
for (auto *U : users())
|
||||
if (auto *GRI = dyn_cast<GCResultInst>(U))
|
||||
return GRI;
|
||||
return nullptr;
|
||||
}
|
||||
/// Returns pair of boolean flags. The first one is true is there is
|
||||
/// a gc.result intrinsic in the same block as statepoint. The second flag
|
||||
/// is true if there is an intrinsic outside of the block with statepoint.
|
||||
inline std::pair<bool, bool> getGCResultLocality() const;
|
||||
};
|
||||
|
||||
/// Common base class for representing values projected from a statepoint.
|
||||
|
@ -329,6 +325,18 @@ std::vector<const GCRelocateInst *> GCStatepointInst::getGCRelocates() const {
|
|||
return Result;
|
||||
}
|
||||
|
||||
std::pair<bool, bool> GCStatepointInst::getGCResultLocality() const {
|
||||
std::pair<bool, bool> Res(false, false);
|
||||
for (auto *U : users())
|
||||
if (auto *GRI = dyn_cast<GCResultInst>(U)) {
|
||||
if (GRI->getParent() == this->getParent())
|
||||
Res.first = true;
|
||||
else
|
||||
Res.second = true;
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
/// Call sites that get wrapped by a gc.statepoint (currently only in
|
||||
/// RewriteStatepointsForGC and potentially in other passes in the future) can
|
||||
/// have attributes that describe properties of gc.statepoint call they will be
|
||||
|
|
|
@ -1076,23 +1076,25 @@ SelectionDAGBuilder::LowerStatepoint(const GCStatepointInst &I,
|
|||
SDValue ReturnValue = LowerAsSTATEPOINT(SI);
|
||||
|
||||
// Export the result value if needed
|
||||
const GCResultInst *GCResult = I.getGCResult();
|
||||
const std::pair<bool, bool> GCResultLocality = I.getGCResultLocality();
|
||||
Type *RetTy = I.getActualReturnType();
|
||||
|
||||
if (RetTy->isVoidTy() || !GCResult) {
|
||||
if (RetTy->isVoidTy() ||
|
||||
(!GCResultLocality.first && !GCResultLocality.second)) {
|
||||
// The return value is not needed, just generate a poison value.
|
||||
setValue(&I, DAG.getIntPtrConstant(-1, getCurSDLoc()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (GCResult->getParent() == I.getParent()) {
|
||||
if (GCResultLocality.first) {
|
||||
// Result value will be used in a same basic block. Don't export it or
|
||||
// perform any explicit register copies. The gc_result will simply grab
|
||||
// this value.
|
||||
setValue(&I, ReturnValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GCResultLocality.second)
|
||||
return;
|
||||
// Result value will be used in a different basic block so we need to export
|
||||
// it now. Default exporting mechanism will not work here because statepoint
|
||||
// call has a different type than the actual call. It means that by default
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
||||
; RUN: llc -verify-machineinstrs < %s | FileCheck %s
|
||||
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
define void @quux() gc "statepoint-example" {
|
||||
; CHECK-LABEL: quux:
|
||||
; CHECK: # %bb.0: # %bb1
|
||||
; CHECK-NEXT: pushq %rax
|
||||
; CHECK-NEXT: .cfi_def_cfa_offset 16
|
||||
; CHECK-NEXT: movl $4, %esi
|
||||
; CHECK-NEXT: callq wombat@PLT
|
||||
; CHECK-NEXT: .Ltmp0:
|
||||
; CHECK-NEXT: popq %rax
|
||||
; CHECK-NEXT: .cfi_def_cfa_offset 8
|
||||
; CHECK-NEXT: retq
|
||||
bb1:
|
||||
%tmp = call token (i64, i32, i8 addrspace(1)* (i8 addrspace(1)*, i32)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_p1i8p1i8i32f(i64 2882400000, i32 0, i8 addrspace(1)* (i8 addrspace(1)*, i32)* nonnull @wombat, i32 2, i32 0, i8 addrspace(1)* undef, i32 4, i32 0, i32 0) [ "gc-live"() ]
|
||||
%tmp2 = tail call i8 addrspace(1)* @llvm.experimental.gc.result.p1i8(token %tmp)
|
||||
br label %bb2
|
||||
|
||||
bb2:
|
||||
%tmp5 = tail call i8 addrspace(1)* @llvm.experimental.gc.result.p1i8(token %tmp)
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i8 addrspace(1)* @wombat(i8 addrspace(1)*, i32)
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare i8 addrspace(1)* @llvm.experimental.gc.result.p1i8(token) #0
|
||||
declare token @llvm.experimental.gc.statepoint.p0f_p1i8p1i8i32f(i64, i32, i8 addrspace(1)* (i8 addrspace(1)*, i32)*, i32, i32, ...)
|
||||
attributes #0 = { nounwind readnone }
|
Loading…
Reference in New Issue