Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -4375,6 +4375,28 @@ /// Improvements for call, callbr and invoke instructions. Instruction *InstCombiner::visitCallBase(CallBase &Call) { + // Special handling of statepoint intrinsic. + // 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 if they can be simplified to null. + // This is mostly compile time safe. Actually C can be transformed on the next + // iteration if InstCombine while we want to handle all gc.relocate chains in + // one iteraton. + if (auto *Calledfunction = Call.getCalledFunction()) + if (Calledfunction->getIntrinsicID() == + Intrinsic::experimental_gc_statepoint) + for (User *U : Call.users()) + if (auto *GCR = dyn_cast(U)) + if (isa(U->getType())) + if (isa(GCR->getDerivedPtr())) + Worklist.add(GCR); + if (isAllocationFn(&Call, &TLI)) annotateAnyAllocSite(Call, &TLI); Index: llvm/test/Transforms/InstCombine/statepoint-null.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/statepoint-null.ll @@ -0,0 +1,33 @@ +; 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 addrspace(1)* null) + %pnew = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token, i32 7, i32 7) + br label %merge + +merge: + %pnew_phi = phi i32 addrspace(1)* [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 addrspace(1)* %pnew_phi) + %pnew2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %safepoint_token2, i32 7, i32 7) + %cmp = icmp eq i32 addrspace(1)* %pnew2, null + ret i1 %cmp +; CHECK-LABEL: test_null +; CHECK-NOT: %pnew +; CHECK-NOT: %pnew2 +; CHECK: ret i1 true +} + +declare token @llvm.experimental.gc.statepoint.p0f_isVoidf(i64, i32, void ()*, i32, i32, ...) +declare i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token, i32, i32) #3