forked from llvm-mirror/llvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[statepoints][experimental] Add support for live-in semantics of valu…
…es in deopt bundles This is a first step towards supporting deopt value lowering and reporting entirely with the register allocator. I hope to build on this in the near future to support live-on-return semantics, but I have a use case which allows me to test and investigate code quality with just the live-in semantics so I've chosen to start there. For those curious, my use cases is our implementation of the "__llvm_deoptimize" function we bind to @llvm.deoptimize. I'm choosing not to hard code that fact in the patch and instead make it configurable via function attributes. The basic approach here is modelled on what is done for the "Live In" values on stackmaps and patchpoints. (A secondary goal here is to remove one of the last barriers to merging the pseudo instructions.) We start by adding the operands directly to the STATEPOINT SDNode. Once we've lowered to MI, we extend the remat logic used by the register allocator to fold virtual register uses into StackMap::Indirect entries as needed. This does rely on the fact that the register allocator rematerializes. If it didn't along some code path, we could end up with more vregs than physical registers and fail to allocate. Today, we *only* fold in the register allocator. This can create some weird effects when combined with arguments passed on the stack because we don't fold them appropriately. I have an idea how to fix that, but it needs this patch in place to work on that effectively. (There's some weird interaction with the scheduler as well, more investigation needed.) My near term plan is to land this patch off-by-default, experiment in my local tree to identify any correctness issues and then start fixing codegen problems one by one as I find them. Once I have the live-in lowering fully working (both correctness and code quality), I'm hoping to move on to the live-on-return semantics. Note: I don't have any *known* miscompiles with this patch enabled, but I'm pretty sure I'll find at least a couple. Thus, the "experimental" tag and the fact it's off by default. Differential Revision: https://reviews.llvm.org/D24000 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280250 91177308-0d34-0410-b5e6-96231b3b80d8
- Loading branch information
Showing
6 changed files
with
233 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
; RUN: llc -O3 < %s | FileCheck %s | ||
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" | ||
target triple = "x86_64-apple-macosx10.11.0" | ||
|
||
declare void @bar() #0 | ||
declare void @baz() | ||
|
||
define void @test1(i32 %a) gc "statepoint-example" { | ||
entry: | ||
; We expect the argument to be passed in an extra register to bar | ||
; CHECK-LABEL: test1 | ||
; CHECK: pushq %rax | ||
; CHECK-NEXT: Ltmp0: | ||
; CHECK-NEXT: .cfi_def_cfa_offset 16 | ||
; CHECK-NEXT: callq _bar | ||
%statepoint_token1 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 1, i32 %a) | ||
ret void | ||
} | ||
|
||
define void @test2(i32 %a, i32 %b) gc "statepoint-example" { | ||
entry: | ||
; Because the first call clobbers esi, we have to move the values into | ||
; new registers. Note that they stay in the registers for both calls. | ||
; CHECK-LABEL: @test2 | ||
; CHECK: movl %esi, %ebx | ||
; CHECK-NEXT: movl %edi, %ebp | ||
; CHECK-NEXT: callq _bar | ||
call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 2, i32 %a, i32 %b) | ||
call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 2, i32 %b, i32 %a) | ||
ret void | ||
} | ||
|
||
define void @test3(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, i32 %i) gc "statepoint-example" { | ||
entry: | ||
; TODO: We should have folded the reload into the statepoint. | ||
; CHECK-LABEL: @test3 | ||
; CHECK: movl 32(%rsp), %r10d | ||
; CHECK-NEXT: movl 24(%rsp), %r11d | ||
; CHECK-NEXT: movl 16(%rsp), %eax | ||
; CHECK-NEXT: callq _bar | ||
%statepoint_token1 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 9, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, i32 %i) | ||
ret void | ||
} | ||
|
||
; This case just confirms that we don't crash when given more live values | ||
; than registers. This is a case where we *have* to use a stack slot. | ||
define void @test4(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p, i32 %q, i32 %r, i32 %s, i32 %t, i32 %u, i32 %v, i32 %w, i32 %x, i32 %y, i32 %z) gc "statepoint-example" { | ||
entry: | ||
; TODO: We should have folded the reload into the statepoint. | ||
; CHECK-LABEL: test4 | ||
; CHECK: pushq %r15 | ||
; CHECK: pushq %r14 | ||
; CHECK: pushq %r13 | ||
; CHECK: pushq %r12 | ||
; CHECK: pushq %rbx | ||
; CHECK: pushq %rax | ||
; CHECK: movl 128(%rsp), %r13d | ||
; CHECK-NEXT: movl 120(%rsp), %r12d | ||
; CHECK-NEXT: movl 112(%rsp), %r15d | ||
; CHECK-NEXT: movl 104(%rsp), %r14d | ||
; CHECK-NEXT: movl 96(%rsp), %ebp | ||
; CHECK-NEXT: movl 88(%rsp), %ebx | ||
; CHECK-NEXT: movl 80(%rsp), %r11d | ||
; CHECK-NEXT: movl 72(%rsp), %r10d | ||
; CHECK-NEXT: movl 64(%rsp), %eax | ||
; CHECK-NEXT: callq _bar | ||
%statepoint_token1 = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 26, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h, i32 %i, i32 %j, i32 %k, i32 %l, i32 %m, i32 %n, i32 %o, i32 %p, i32 %q, i32 %r, i32 %s, i32 %t, i32 %u, i32 %v, i32 %w, i32 %x, i32 %y, i32 %z) | ||
ret void | ||
} | ||
|
||
; A live-through gc-value must be spilled even if it is also a live-in deopt | ||
; value. For live-in, we could technically report the register copy, but from | ||
; a code quality perspective it's better to reuse the required stack slot so | ||
; as to put less stress on the register allocator for no benefit. | ||
define i32 addrspace(1)* @test5(i32 %a, i32 addrspace(1)* %p) gc "statepoint-example" { | ||
entry: | ||
; CHECK-LABEL: test5 | ||
; CHECK: movq %rsi, (%rsp) | ||
; CHECK-NEXT: callq _bar | ||
%token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 1, i32 %a, i32 addrspace(1)* %p, i32 addrspace(1)* %p) | ||
%p2 = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %token, i32 9, i32 9) | ||
ret i32 addrspace(1)* %p2 | ||
} | ||
|
||
; Show the interaction of live-through spilling followed by live-in. | ||
define void @test6(i32 %a) gc "statepoint-example" { | ||
entry: | ||
; TODO: We could have reused the previous spill slot at zero additional cost. | ||
; CHECK-LABEL: test6 | ||
; CHECK: movl %edi, %ebx | ||
; CHECK: movl %ebx, 12(%rsp) | ||
; CHECK-NEXT: callq _baz | ||
; CHECK-NEXT: Ltmp30: | ||
; CHECK-NEXT: callq _bar | ||
call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @baz, i32 0, i32 0, i32 0, i32 1, i32 %a) | ||
call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 1, i32 %a) | ||
ret void | ||
} | ||
|
||
|
||
; CHECK: Ltmp1-_test1 | ||
; CHECK: .byte 1 | ||
; CHECK-NEXT: .byte 4 | ||
; CHECK-NEXT: .short 5 | ||
; CHECK-NEXT: .long 0 | ||
|
||
; CHECK: Ltmp7-_test2 | ||
; CHECK: .byte 1 | ||
; CHECK-NEXT: .byte 4 | ||
; CHECK-NEXT: .short 6 | ||
; CHECK-NEXT: .long 0 | ||
; CHECK: .byte 1 | ||
; CHECK-NEXT: .byte 4 | ||
; CHECK-NEXT: .short 3 | ||
; CHECK-NEXT: .long 0 | ||
; CHECK: Ltmp8-_test2 | ||
; CHECK: .byte 1 | ||
; CHECK-NEXT: .byte 4 | ||
; CHECK-NEXT: .short 3 | ||
; CHECK-NEXT: .long 0 | ||
; CHECK: .byte 1 | ||
; CHECK-NEXT: .byte 4 | ||
; CHECK-NEXT: .short 6 | ||
; CHECK-NEXT: .long 0 | ||
|
||
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) | ||
|
||
|
||
attributes #0 = { "deopt-lowering"="live-in" } | ||
attributes #1 = { "deopt-lowering"="live-through" } |
23 changes: 23 additions & 0 deletions
23
test/Transforms/RewriteStatepointsForGC/deopt-lowering-attrs.ll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
; RUN: opt -rewrite-statepoints-for-gc -S < %s | FileCheck %s | ||
; Check that the "deopt-lowering" function attribute gets transcoded into | ||
; flags on the resulting statepoint | ||
|
||
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" | ||
target triple = "x86_64-apple-macosx10.11.0" | ||
|
||
declare void @foo() | ||
declare void @bar() "deopt-lowering"="live-in" | ||
declare void @baz() "deopt-lowering"="live-through" | ||
|
||
define void @test1() gc "statepoint-example" { | ||
; CHECK-LABEL: @test1( | ||
; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 1, i32 57) | ||
; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @bar, i32 0, i32 2, i32 0, i32 1, i32 42) | ||
; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @baz, i32 0, i32 0, i32 0, i32 1, i32 13) | ||
|
||
entry: | ||
call void @foo() [ "deopt"(i32 57) ] | ||
call void @bar() [ "deopt"(i32 42) ] | ||
call void @baz() [ "deopt"(i32 13) ] | ||
ret void | ||
} |