Skip to content

Commit

Permalink
[InstCombine] Handle gc.relocate(null) in one iteration
Browse files Browse the repository at this point in the history
InstCombine adds users of transformed instruction to working list to
process on the same iteration. However gc.relocate may have a hidden
user (next gc.relocate) which is connected through gc.statepoint intrinsic and
there is no direct def-use chain between them.

In this case if the next gc.relocation is already processed it will not be added
to worklist and will not be able to be processed on the same iteration.
Let's we have the following case:
A = gc.relocate(null)
B = statepoint(A)
C = gc.relocate(B, hidden(A))
If C is already considered then after replacement of A with null, statepoint B
instruction will be added to the queue but not C.
C can be processed only on the next iteration.

If the chain of relocation is pretty long the many iteration may be required.
This change is to reduce the number of iteration to meet the latest changes
related to reducing infinite loop threshold.

This is a quick (not best) fix. In the follow up patches I plan to move gc relocation
handling into statepoint handler. This should also help to remove unused gc live
entries in statepoint bundle.

Reviewers: reames, dantrushin
Reviewed By: reames
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D75598
  • Loading branch information
Serguei Katkov committed Aug 13, 2020
1 parent b36e22d commit 98ba0a5
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
21 changes: 21 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
Expand Up @@ -1474,6 +1474,27 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
AC.updateAffectedValues(II);
break;
}
case Intrinsic::experimental_gc_statepoint: {
auto &GCSP = *cast<GCStatepointInst>(II);
// Let's we have the following case:
// A = gc.relocate(null)
// B = statepoint(A)
// C = gc.relocate(A)
// A will be substituted with null and its user B will be added to worklist.
// Statepoint B is not simplified and if C was considered before it will be
// re-considered after simplification of A.
// To resolve this case while processing statepoint B we add all gc.relocate
// users to worklist to give a chance to be simplified to null.
// This is to reduce the number of InstCombine iteration.
// Actually C can be transformed on the next iteration.
// chains in one iteration.
// TODO: we can handle relocation here, it will reduce the number of
// relocations to re-consider and also helps to reduce the number of
// gc live pointers in statepoint instruction bundle.
for (const GCRelocateInst *Reloc : GCSP.getGCRelocates())
Worklist.add(const_cast<GCRelocateInst *>(Reloc));
break;
}
case Intrinsic::experimental_gc_relocate: {
auto &GCR = *cast<GCRelocateInst>(II);

Expand Down
56 changes: 56 additions & 0 deletions llvm/test/Transforms/InstCombine/statepoint-iter.ll
@@ -0,0 +1,56 @@
; RUN: opt < %s -instcombine -instcombine-max-iterations=1 -S | FileCheck %s
; These tests check the optimizations specific to
; pointers being relocated at a statepoint.


declare void @func()

define i1 @test_null(i1 %cond) gc "statepoint-example" {
entry:
br i1 %cond, label %left, label %right

right:
br label %merge

left:
%safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32* null)
%pnew = call i32* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 7)
br label %merge

merge:
%pnew_phi = phi i32* [null, %right], [%pnew, %left]
%safepoint_token2 = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32* %pnew_phi)
%pnew2 = call i32* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 7)
%cmp = icmp eq i32* %pnew2, null
ret i1 %cmp
; CHECK-LABEL: test_null
; CHECK-NOT: %pnew
; CHECK-NOT: %pnew2
; CHECK: ret i1 true
}

define i32* @test_undef(i1 %cond) gc "statepoint-example" {
entry:
br i1 %cond, label %left, label %right

right:
br label %merge

left:
%safepoint_token = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32* undef)
%pnew = call i32* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 7)
br label %merge

merge:
%pnew_phi = phi i32* [undef, %right], [%pnew, %left]
%safepoint_token2 = tail call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* @func, i32 0, i32 0, i32 0, i32 0, i32* %pnew_phi)
%pnew2 = call i32* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 7)
ret i32* %pnew2
; CHECK-LABEL: test_undef
; CHECK-NOT: %pnew
; CHECK-NOT: %pnew2
; CHECK: ret i32* undef
}

declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...)
declare i32* @llvm.experimental.gc.relocate.p1i32(token, i32, i32)

0 comments on commit 98ba0a5

Please sign in to comment.