llvm-project/llvm/test/Transforms/RewriteStatepointsForGC/rematerialize-derived-pointers-at-uses.ll
Denis Antrushin 86ed0daae7 [RS4GC] Rematerialize derived pointers before uses.
Introduce an option to rematerialize derived pointers immediately
before their uses instead of after every statepoint. This can be
beneficial when pointer is live across many statepoints but has
few uses.
Initial implementation is simple and rematerializes derived pointer
before every use, even if there are several uses in the same block
or rematerialization instructions can be hoisted etc.
Transformation is considered profitable if we would insert less
instructions than we would insert after every live statepoint.

Depends on D138910, D138911

Reviewed By: anna, skatkov

Differential Revision: https://reviews.llvm.org/D138912
2022-12-27 17:08:57 +03:00

222 lines
13 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes='rewrite-statepoints-for-gc,verify<safepoint-ir>' -S | FileCheck %s
declare void @use_obj(ptr addrspace(1)) "gc-leaf-function"
declare void @do_safepoint()
; Profitable test
define i32 @test_remat(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
; CHECK-LABEL: @test_remat(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
; CHECK-NEXT: [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
; CHECK: here:
; CHECK-NEXT: [[STATEPOINT_TOKEN1:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED2:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN1]], i32 0, i32 0)
; CHECK-NEXT: br label [[MERGE:%.*]]
; CHECK: there:
; CHECK-NEXT: [[STATEPOINT_TOKEN3:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED4:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN3]], i32 0, i32 0)
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE_RELOCATED2]], [[HERE]] ], [ [[BASE_RELOCATED4]], [[THERE]] ]
; CHECK-NEXT: [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 16
; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT]], align 4
; CHECK-NEXT: ret i32 [[RET]]
;
entry:
%derived = getelementptr i32, ptr addrspace(1) %base, i32 16
call void @do_safepoint() [ "deopt"() ]
br i1 %cond, label %here, label %there
here:
call void @do_safepoint() [ "deopt"() ]
br label %merge
there:
call void @do_safepoint() [ "deopt"() ]
br label %merge
merge:
%ret = load i32, ptr addrspace(1) %derived
ret i32 %ret
}
; Unprofitable test
define i32 @test_many_uses(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
; CHECK-LABEL: @test_many_uses(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
; CHECK-NEXT: [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 16
; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
; CHECK: here:
; CHECK-NEXT: [[STATEPOINT_TOKEN3:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED4:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN3]], i32 0, i32 0)
; CHECK-NEXT: [[DERIVED_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED4]], i32 16
; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT1]])
; CHECK-NEXT: br label [[MERGE:%.*]]
; CHECK: there:
; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT]])
; CHECK-NEXT: [[STATEPOINT_TOKEN5:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED6:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN5]], i32 0, i32 0)
; CHECK-NEXT: [[DERIVED_REMAT2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED6]], i32 16
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[DERIVED_REMAT1]], [[HERE]] ], [ [[DERIVED_REMAT2]], [[THERE]] ]
; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DOT0]])
; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[DOT0]], align 4
; CHECK-NEXT: ret i32 [[RET]]
;
entry:
%derived = getelementptr i32, ptr addrspace(1) %base, i32 16
call void @do_safepoint() [ "deopt" () ]
br i1 %cond, label %here, label %there
here:
call void @do_safepoint() [ "deopt"() ]
call void @use_obj(ptr addrspace(1) %derived)
br label %merge
there:
call void @use_obj(ptr addrspace(1) %derived)
call void @do_safepoint() [ "deopt"() ]
br label %merge
merge:
call void @use_obj(ptr addrspace(1) %derived)
%ret = load i32, ptr addrspace(1) %derived
ret i32 %ret
}
; Remat before phi - not implemented
define i32 @test_phi(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
; CHECK-LABEL: @test_phi(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DERIVED1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
; CHECK-NEXT: [[DERIVED2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE]], i32 32
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
; CHECK-NEXT: [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: [[DERIVED2_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 32
; CHECK-NEXT: [[DERIVED1_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 16
; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
; CHECK: here:
; CHECK-NEXT: br label [[MERGE:%.*]]
; CHECK: there:
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
; CHECK-NEXT: [[PHI1:%.*]] = phi ptr addrspace(1) [ [[DERIVED1_REMAT]], [[HERE]] ], [ [[DERIVED2_REMAT]], [[THERE]] ]
; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[PHI1]], align 4
; CHECK-NEXT: ret i32 [[RET]]
;
entry:
%derived1 = getelementptr i32, ptr addrspace(1) %base, i32 16
%derived2 = getelementptr i32, ptr addrspace(1) %base, i32 32
call void @do_safepoint() [ "deopt"() ]
br i1 %cond, label %here, label %there
here:
br label %merge
there:
br label %merge
merge:
%phi1 = phi ptr addrspace(1) [ %derived1, %here ], [ %derived2, %there ]
%ret = load i32, ptr addrspace(1) %phi1
ret i32 %ret
}
; Several uses per block
; TODO: We could rematerialize once per block for several uses if there is not statepoint between
define i32 @test_same_block_with_statepoint(ptr addrspace(1) %base, i1 %cond) gc "statepoint-example" {
; CHECK-LABEL: @test_same_block_with_statepoint(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DERIVED:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE:%.*]], i32 16
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE]]) ]
; CHECK-NEXT: [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: br i1 [[COND:%.*]], label [[HERE:%.*]], label [[THERE:%.*]]
; CHECK: here:
; CHECK-NEXT: [[DERIVED_REMAT3:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED]], i32 16
; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT3]])
; CHECK-NEXT: [[STATEPOINT_TOKEN4:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED5:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN4]], i32 0, i32 0)
; CHECK-NEXT: [[DERIVED_REMAT2:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED5]], i32 16
; CHECK-NEXT: call void @use_obj(ptr addrspace(1) [[DERIVED_REMAT2]])
; CHECK-NEXT: [[DERIVED_REMAT1:%.*]] = getelementptr i32, ptr addrspace(1) [[BASE_RELOCATED5]], i32 16
; CHECK-NEXT: [[DUMMY:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT1]], align 4
; CHECK-NEXT: [[STATEPOINT_TOKEN6:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED5]]) ]
; CHECK-NEXT: [[BASE_RELOCATED7:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN6]], i32 0, i32 0)
; CHECK-NEXT: br label [[MERGE:%.*]]
; CHECK: there:
; CHECK-NEXT: [[STATEPOINT_TOKEN8:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED9:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN8]], i32 0, i32 0)
; CHECK-NEXT: br label [[MERGE]]
; CHECK: merge:
; CHECK-NEXT: [[DOT0:%.*]] = phi ptr addrspace(1) [ [[BASE_RELOCATED7]], [[HERE]] ], [ [[BASE_RELOCATED9]], [[THERE]] ]
; CHECK-NEXT: [[DERIVED_REMAT:%.*]] = getelementptr i32, ptr addrspace(1) [[DOT0]], i32 16
; CHECK-NEXT: [[RET:%.*]] = load i32, ptr addrspace(1) [[DERIVED_REMAT]], align 4
; CHECK-NEXT: ret i32 [[RET]]
;
entry:
%derived = getelementptr i32, ptr addrspace(1) %base, i32 16
call void @do_safepoint() [ "deopt" () ]
br i1 %cond, label %here, label %there
here:
call void @use_obj(ptr addrspace(1) %derived)
call void @do_safepoint() [ "deopt"() ]
call void @use_obj(ptr addrspace(1) %derived)
%dummy = load i32, ptr addrspace(1) %derived
call void @do_safepoint() [ "deopt" () ]
br label %merge
there:
call void @do_safepoint() [ "deopt"() ]
br label %merge
merge:
%ret = load i32, ptr addrspace(1) %derived
ret i32 %ret
}
; Test long chain with sub-chain rematerialized
; TODO: If we rematerialized longer chain first (%v4), then shorter on (%v0) would become dead
define void @test_chain(ptr addrspace(1) %base, i1 %cond1) gc "statepoint-example" {
; CHECK-LABEL: @test_chain(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[V0:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE:%.*]], i64 16
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(ptr addrspace(1) [[BASE]]), "gc-live"(ptr addrspace(1) [[BASE]]) ]
; CHECK-NEXT: [[BASE_RELOCATED:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN]], i32 0, i32 0)
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[BLOCK3:%.*]], label [[COMMON_RET:%.*]]
; CHECK: block3:
; CHECK-NEXT: [[V0_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE_RELOCATED]], i64 16
; CHECK-NEXT: [[V4:%.*]] = getelementptr i8, ptr addrspace(1) [[V0_REMAT]], i64 70
; CHECK-NEXT: [[STATEPOINT_TOKEN1:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(void ()) @do_safepoint, i32 0, i32 0, i32 0, i32 0) [ "deopt"(ptr addrspace(1) [[BASE_RELOCATED]]), "gc-live"(ptr addrspace(1) [[BASE_RELOCATED]]) ]
; CHECK-NEXT: [[BASE_RELOCATED2:%.*]] = call coldcc ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token [[STATEPOINT_TOKEN1]], i32 0, i32 0)
; CHECK-NEXT: [[V0_REMAT_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[BASE_RELOCATED2]], i64 16
; CHECK-NEXT: [[V4_REMAT:%.*]] = getelementptr i8, ptr addrspace(1) [[V0_REMAT_REMAT]], i64 70
; CHECK-NEXT: [[V5:%.*]] = load atomic i8, ptr addrspace(1) [[V4_REMAT]] unordered, align 2
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: common.ret:
; CHECK-NEXT: ret void
;
entry:
%v0 = getelementptr i8, ptr addrspace(1) %base, i64 16
call void @do_safepoint() [ "deopt"(ptr addrspace(1) %base) ]
br i1 %cond1, label %block3, label %common.ret
block3:
%v4 = getelementptr i8, ptr addrspace(1) %v0, i64 70
call void @do_safepoint() [ "deopt"(ptr addrspace(1) %base) ]
%v5 = load atomic i8, ptr addrspace(1) %v4 unordered, align 2
br label %common.ret
common.ret:
ret void
}