Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reapply "[GVN] Prevent LoadPRE from hoisting across instructions that…
… don't pass control flow to successors" This patch fixes the miscompile that happens when PRE hoists loads across guards and other instructions that don't always pass control flow to their successors. PRE is now prohibited to hoist across such instructions because there is no guarantee that the load standing after such instruction is still valid before such instruction. For example, a load from under a guard may be invalid before the guard in the following case: int array[LEN]; ... guard(0 <= index && index < LEN); use(array[index]); Differential Revision: https://reviews.llvm.org/D37460 llvm-svn: 316975
- Loading branch information
Max Kazantsev
committed
Oct 31, 2017
1 parent
39a8dbf
commit 488ec97
Showing
6 changed files
with
533 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
; RUN: opt < %s -basicaa -gvn -enable-load-pre -S | FileCheck %s | ||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" | ||
|
||
declare void @llvm.experimental.guard(i1, ...) | ||
|
||
; This is a motivating example on why we prohibit hoisting through guards. | ||
; In the bottom block, we check that the index is within bounds and only access | ||
; the element in this case and deoptimize otherwise. If we hoist the load to a | ||
; place above the guard, it will may lead to out-of-bound array access. | ||
define i32 @test_motivation(i32* %p, i32* %q, i1 %C, i32 %index, i32 %len) { | ||
; CHECK-LABEL: @test_motivation( | ||
block1: | ||
%el1 = getelementptr inbounds i32, i32* %q, i32 %index | ||
%el2 = getelementptr inbounds i32, i32* %p, i32 %index | ||
br i1 %C, label %block2, label %block3 | ||
|
||
block2: | ||
|
||
; CHECK: block2: | ||
; CHECK-NEXT: br | ||
; CHECK-NOT: load | ||
; CHECK-NOT: sge | ||
; CHECK-NOT: slt | ||
; CHECK-NOT: and | ||
br label %block4 | ||
|
||
block3: | ||
store i32 0, i32* %el1 | ||
br label %block4 | ||
|
||
block4: | ||
|
||
; CHECK: block4: | ||
; CHECK: %cond1 = icmp sge i32 %index, 0 | ||
; CHECK-NEXT: %cond2 = icmp slt i32 %index, %len | ||
; CHECK-NEXT: %in.bounds = and i1 %cond1, %cond2 | ||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %in.bounds) | ||
; CHECK-NEXT: %PRE = load i32, i32* %P2 | ||
; CHECK: ret i32 %PRE | ||
|
||
%P2 = phi i32* [%el2, %block3], [%el1, %block2] | ||
%cond1 = icmp sge i32 %index, 0 | ||
%cond2 = icmp slt i32 %index, %len | ||
%in.bounds = and i1 %cond1, %cond2 | ||
call void (i1, ...) @llvm.experimental.guard(i1 %in.bounds) [ "deopt"() ] | ||
%PRE = load i32, i32* %P2 | ||
ret i32 %PRE | ||
} | ||
|
||
; Guard in load's block that is above the load should prohibit the PRE. | ||
define i32 @test_guard_01(i32* %p, i32* %q, i1 %C, i1 %G) { | ||
; CHECK-LABEL: @test_guard_01( | ||
block1: | ||
br i1 %C, label %block2, label %block3 | ||
|
||
block2: | ||
|
||
; CHECK: block2: | ||
; CHECK-NEXT: br | ||
; CHECK-NOT: load | ||
|
||
br label %block4 | ||
|
||
block3: | ||
store i32 0, i32* %p | ||
br label %block4 | ||
|
||
block4: | ||
|
||
; CHECK: block4: | ||
; CHECK: call void (i1, ...) @llvm.experimental.guard(i1 %G) | ||
; CHECK-NEXT: load | ||
; CHECK: ret i32 | ||
|
||
%P2 = phi i32* [%p, %block3], [%q, %block2] | ||
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ] | ||
%PRE = load i32, i32* %P2 | ||
ret i32 %PRE | ||
} | ||
|
||
; Guard in load's block that is below the load should not prohibit the PRE. | ||
define i32 @test_guard_02(i32* %p, i32* %q, i1 %C, i1 %G) { | ||
; CHECK-LABEL: @test_guard_02( | ||
block1: | ||
br i1 %C, label %block2, label %block3 | ||
|
||
block2: | ||
|
||
; CHECK: block2: | ||
; CHECK-NEXT: load i32, i32* %q | ||
|
||
br label %block4 | ||
|
||
block3: | ||
store i32 0, i32* %p | ||
br label %block4 | ||
|
||
block4: | ||
|
||
; CHECK: block4: | ||
; CHECK-NEXT: phi i32 [ | ||
; CHECK-NEXT: phi i32* [ | ||
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %G) | ||
; CHECK-NOT: load | ||
; CHECK: ret i32 | ||
|
||
%P2 = phi i32* [%p, %block3], [%q, %block2] | ||
%PRE = load i32, i32* %P2 | ||
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ] | ||
ret i32 %PRE | ||
} | ||
|
||
; Guard above the load's block should prevent PRE from hoisting through it. | ||
define i32 @test_guard_03(i32* %p, i32* %q, i1 %C, i1 %G) { | ||
; CHECK-LABEL: @test_guard_03( | ||
block1: | ||
br i1 %C, label %block2, label %block3 | ||
|
||
block2: | ||
|
||
; CHECK: block2: | ||
; CHECK-NEXT: br | ||
; CHECK-NOT: load | ||
|
||
br label %block4 | ||
|
||
block3: | ||
store i32 0, i32* %p | ||
br label %block4 | ||
|
||
block4: | ||
|
||
; CHECK: block4: | ||
; CHECK-NEXT: phi i32* | ||
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %G) | ||
; CHECK-NEXT: load | ||
; CHECK-NEXT: ret i32 | ||
|
||
%P2 = phi i32* [%p, %block3], [%q, %block2] | ||
call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ] | ||
br label %block5 | ||
|
||
block5: | ||
%PRE = load i32, i32* %P2 | ||
ret i32 %PRE | ||
} |
Oops, something went wrong.