
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
222 lines
13 KiB
LLVM
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
|
|
}
|