diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -1474,6 +1474,27 @@ AC.updateAffectedValues(II); break; } + case Intrinsic::experimental_gc_statepoint: { + auto &GCSP = *cast(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(Reloc)); + break; + } case Intrinsic::experimental_gc_relocate: { auto &GCR = *cast(II); diff --git a/llvm/test/Transforms/InstCombine/statepoint-iter.ll b/llvm/test/Transforms/InstCombine/statepoint-iter.ll new file mode 100644 --- /dev/null +++ b/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)