147 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			LLVM
		
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			LLVM
		
	
	
	
; 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
 | 
						|
}
 |