Guozhi Wei 84bcfa0e1b [GVN] Improve PRE on load instructions
This patch implements the enhancement proposed by
https://github.com/llvm/llvm-project/issues/59312.

Suppose we have following code

   v0 = load %addr
   br %LoadBB

LoadBB:
   v1 = load %addr
   ...

PredBB:
   ...
   br %cond, label %LoadBB, label %SuccBB

SuccBB:
   v2 = load %addr
   ...

Instruction v1 in LoadBB is partially redundant, edge (PredBB, LoadBB) is a
critical edge. SuccBB is another successor of PredBB, it contains another load
v2 which is identical to v1. Current GVN splits the critical edge
(PredBB, LoadBB) and inserts a new load in it. A better method is move the load
of v2 into PredBB, then v1 can be changed to a PHI instruction.

If there are two or more similar predecessors, like the test case in the bug
entry, current GVN simply gives up because otherwise it needs to split multiple
critical edges. But we can move all loads in successor blocks into predecessors.

Differential Revision: https://reviews.llvm.org/D141712
2023-06-06 19:45:34 +00:00

1103 lines
35 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=gvn -enable-load-pre -S | FileCheck %s
; RUN: opt < %s -aa-pipeline=basic-aa -passes="gvn<load-pre>" -enable-load-pre=false -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"
define i32 @test1(ptr %p, i1 %C) {
; CHECK-LABEL: @test1(
; CHECK-NEXT: block1:
; CHECK-NEXT: br i1 [[C:%.*]], label [[BLOCK2:%.*]], label [[BLOCK3:%.*]]
; CHECK: block2:
; CHECK-NEXT: [[PRE_PRE:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[BLOCK4:%.*]]
; CHECK: block3:
; CHECK-NEXT: store i32 0, ptr [[P]], align 4
; CHECK-NEXT: br label [[BLOCK4]]
; CHECK: block4:
; CHECK-NEXT: [[PRE:%.*]] = phi i32 [ 0, [[BLOCK3]] ], [ [[PRE_PRE]], [[BLOCK2]] ]
; CHECK-NEXT: ret i32 [[PRE]]
;
block1:
br i1 %C, label %block2, label %block3
block2:
br label %block4
block3:
store i32 0, ptr %p
br label %block4
block4:
%PRE = load i32, ptr %p
ret i32 %PRE
}
; This is a simple phi translation case.
define i32 @test2(ptr %p, ptr %q, i1 %C) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: block1:
; CHECK-NEXT: br i1 [[C:%.*]], label [[BLOCK2:%.*]], label [[BLOCK3:%.*]]
; CHECK: block2:
; CHECK-NEXT: [[PRE_PRE:%.*]] = load i32, ptr [[Q:%.*]], align 4
; CHECK-NEXT: br label [[BLOCK4:%.*]]
; CHECK: block3:
; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[BLOCK4]]
; CHECK: block4:
; CHECK-NEXT: [[PRE:%.*]] = phi i32 [ 0, [[BLOCK3]] ], [ [[PRE_PRE]], [[BLOCK2]] ]
; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[P]], [[BLOCK3]] ], [ [[Q]], [[BLOCK2]] ]
; CHECK-NEXT: ret i32 [[PRE]]
;
block1:
br i1 %C, label %block2, label %block3
block2:
br label %block4
block3:
store i32 0, ptr %p
br label %block4
block4:
%P2 = phi ptr [%p, %block3], [%q, %block2]
%PRE = load i32, ptr %P2
ret i32 %PRE
}
; This is a PRE case that requires phi translation through a GEP.
define i32 @test3(ptr %p, ptr %q, ptr %Hack, i1 %C) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: block1:
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[Q:%.*]], i32 1
; CHECK-NEXT: store ptr [[B]], ptr [[HACK:%.*]], align 8
; CHECK-NEXT: br i1 [[C:%.*]], label [[BLOCK2:%.*]], label [[BLOCK3:%.*]]
; CHECK: block2:
; CHECK-NEXT: [[PRE_PRE:%.*]] = load i32, ptr [[B]], align 4
; CHECK-NEXT: br label [[BLOCK4:%.*]]
; CHECK: block3:
; CHECK-NEXT: [[A:%.*]] = getelementptr i32, ptr [[P:%.*]], i32 1
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: br label [[BLOCK4]]
; CHECK: block4:
; CHECK-NEXT: [[PRE:%.*]] = phi i32 [ 0, [[BLOCK3]] ], [ [[PRE_PRE]], [[BLOCK2]] ]
; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[P]], [[BLOCK3]] ], [ [[Q]], [[BLOCK2]] ]
; CHECK-NEXT: [[P3:%.*]] = getelementptr i32, ptr [[P2]], i32 1
; CHECK-NEXT: ret i32 [[PRE]]
;
block1:
%B = getelementptr i32, ptr %q, i32 1
store ptr %B, ptr %Hack
br i1 %C, label %block2, label %block3
block2:
br label %block4
block3:
%A = getelementptr i32, ptr %p, i32 1
store i32 0, ptr %A
br label %block4
block4:
%P2 = phi ptr [%p, %block3], [%q, %block2]
%P3 = getelementptr i32, ptr %P2, i32 1
%PRE = load i32, ptr %P3
ret i32 %PRE
}
;; Here the loaded address is available, but the computation is in 'block3'
;; which does not dominate 'block2'.
define i32 @test4(ptr %p, ptr %q, ptr %Hack, i1 %C) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: block1:
; CHECK-NEXT: br i1 [[C:%.*]], label [[BLOCK2:%.*]], label [[BLOCK3:%.*]]
; CHECK: block2:
; CHECK-NEXT: [[P3_PHI_TRANS_INSERT:%.*]] = getelementptr i32, ptr [[Q:%.*]], i32 1
; CHECK-NEXT: [[PRE_PRE:%.*]] = load i32, ptr [[P3_PHI_TRANS_INSERT]], align 4
; CHECK-NEXT: br label [[BLOCK4:%.*]]
; CHECK: block3:
; CHECK-NEXT: [[B:%.*]] = getelementptr i32, ptr [[Q]], i32 1
; CHECK-NEXT: store ptr [[B]], ptr [[HACK:%.*]], align 8
; CHECK-NEXT: [[A:%.*]] = getelementptr i32, ptr [[P:%.*]], i32 1
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: br label [[BLOCK4]]
; CHECK: block4:
; CHECK-NEXT: [[PRE:%.*]] = phi i32 [ 0, [[BLOCK3]] ], [ [[PRE_PRE]], [[BLOCK2]] ]
; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[P]], [[BLOCK3]] ], [ [[Q]], [[BLOCK2]] ]
; CHECK-NEXT: [[P3:%.*]] = getelementptr i32, ptr [[P2]], i32 1
; CHECK-NEXT: ret i32 [[PRE]]
;
block1:
br i1 %C, label %block2, label %block3
block2:
br label %block4
block3:
%B = getelementptr i32, ptr %q, i32 1
store ptr %B, ptr %Hack
%A = getelementptr i32, ptr %p, i32 1
store i32 0, ptr %A
br label %block4
block4:
%P2 = phi ptr [%p, %block3], [%q, %block2]
%P3 = getelementptr i32, ptr %P2, i32 1
%PRE = load i32, ptr %P3
ret i32 %PRE
}
;void test5(int N, ptr G) {
; int j;
; for (j = 0; j < N - 1; j++)
; G[j] = G[j] + G[j+1];
;}
define void @test5(i32 %N, ptr nocapture %G) nounwind ssp {
; CHECK-LABEL: @test5(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 0
; CHECK-NEXT: br i1 [[TMP1]], label [[BB_NPH:%.*]], label [[RETURN:%.*]]
; CHECK: bb.nph:
; CHECK-NEXT: [[TMP:%.*]] = zext i32 [[TMP0]] to i64
; CHECK-NEXT: [[DOTPRE:%.*]] = load double, ptr [[G:%.*]], align 8
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[TMP2:%.*]] = phi double [ [[DOTPRE]], [[BB_NPH]] ], [ [[TMP3:%.*]], [[BB]] ]
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, [[BB_NPH]] ], [ [[TMP6:%.*]], [[BB]] ]
; CHECK-NEXT: [[TMP6]] = add i64 [[INDVAR]], 1
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP6]]
; CHECK-NEXT: [[SCEVGEP7:%.*]] = getelementptr double, ptr [[G]], i64 [[INDVAR]]
; CHECK-NEXT: [[TMP3]] = load double, ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[TMP4:%.*]] = fadd double [[TMP2]], [[TMP3]]
; CHECK-NEXT: store double [[TMP4]], ptr [[SCEVGEP7]], align 8
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP6]], [[TMP]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[RETURN]], label [[BB]]
; CHECK: return:
; CHECK-NEXT: ret void
;
entry:
%0 = add i32 %N, -1
%1 = icmp sgt i32 %0, 0
br i1 %1, label %bb.nph, label %return
bb.nph:
%tmp = zext i32 %0 to i64
br label %bb
bb:
%indvar = phi i64 [ 0, %bb.nph ], [ %tmp6, %bb ]
%tmp6 = add i64 %indvar, 1
%scevgep = getelementptr double, ptr %G, i64 %tmp6
%scevgep7 = getelementptr double, ptr %G, i64 %indvar
%2 = load double, ptr %scevgep7, align 8
%3 = load double, ptr %scevgep, align 8
%4 = fadd double %2, %3
store double %4, ptr %scevgep7, align 8
%exitcond = icmp eq i64 %tmp6, %tmp
br i1 %exitcond, label %return, label %bb
; Should only be one load in the loop.
return:
ret void
}
;void test6(int N, ptr G) {
; int j;
; for (j = 0; j < N - 1; j++)
; G[j+1] = G[j] + G[j+1];
;}
define void @test6(i32 %N, ptr nocapture %G) nounwind ssp {
; CHECK-LABEL: @test6(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 0
; CHECK-NEXT: br i1 [[TMP1]], label [[BB_NPH:%.*]], label [[RETURN:%.*]]
; CHECK: bb.nph:
; CHECK-NEXT: [[TMP:%.*]] = zext i32 [[TMP0]] to i64
; CHECK-NEXT: [[DOTPRE:%.*]] = load double, ptr [[G:%.*]], align 8
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[TMP2:%.*]] = phi double [ [[DOTPRE]], [[BB_NPH]] ], [ [[TMP4:%.*]], [[BB]] ]
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, [[BB_NPH]] ], [ [[TMP6:%.*]], [[BB]] ]
; CHECK-NEXT: [[TMP6]] = add i64 [[INDVAR]], 1
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP6]]
; CHECK-NEXT: [[SCEVGEP7:%.*]] = getelementptr double, ptr [[G]], i64 [[INDVAR]]
; CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[TMP4]] = fadd double [[TMP2]], [[TMP3]]
; CHECK-NEXT: store double [[TMP4]], ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP6]], [[TMP]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[RETURN]], label [[BB]]
; CHECK: return:
; CHECK-NEXT: ret void
;
entry:
%0 = add i32 %N, -1
%1 = icmp sgt i32 %0, 0
br i1 %1, label %bb.nph, label %return
bb.nph:
%tmp = zext i32 %0 to i64
br label %bb
bb:
%indvar = phi i64 [ 0, %bb.nph ], [ %tmp6, %bb ]
%tmp6 = add i64 %indvar, 1
%scevgep = getelementptr double, ptr %G, i64 %tmp6
%scevgep7 = getelementptr double, ptr %G, i64 %indvar
%2 = load double, ptr %scevgep7, align 8
%3 = load double, ptr %scevgep, align 8
%4 = fadd double %2, %3
store double %4, ptr %scevgep, align 8
%exitcond = icmp eq i64 %tmp6, %tmp
br i1 %exitcond, label %return, label %bb
; Should only be one load in the loop.
return:
ret void
}
;void test7(int N, ptr G) {
; long j;
; G[1] = 1;
; for (j = 1; j < N - 1; j++)
; G[j+1] = G[j] + G[j+1];
;}
; This requires phi translation of the adds.
define void @test7(i32 %N, ptr nocapture %G) nounwind ssp {
; CHECK-LABEL: @test7(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds double, ptr [[G:%.*]], i64 1
; CHECK-NEXT: store double 1.000000e+00, ptr [[TMP0]], align 8
; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[N:%.*]], -1
; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[TMP1]], 1
; CHECK-NEXT: br i1 [[TMP2]], label [[BB_NPH:%.*]], label [[RETURN:%.*]]
; CHECK: bb.nph:
; CHECK-NEXT: [[TMP:%.*]] = sext i32 [[TMP1]] to i64
; CHECK-NEXT: [[TMP7:%.*]] = add i64 [[TMP]], -1
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[TMP3:%.*]] = phi double [ 1.000000e+00, [[BB_NPH]] ], [ [[TMP5:%.*]], [[BB]] ]
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, [[BB_NPH]] ], [ [[TMP9:%.*]], [[BB]] ]
; CHECK-NEXT: [[TMP8:%.*]] = add i64 [[INDVAR]], 2
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP8]]
; CHECK-NEXT: [[TMP9]] = add i64 [[INDVAR]], 1
; CHECK-NEXT: [[SCEVGEP10:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP9]]
; CHECK-NEXT: [[TMP4:%.*]] = load double, ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[TMP5]] = fadd double [[TMP3]], [[TMP4]]
; CHECK-NEXT: store double [[TMP5]], ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP9]], [[TMP7]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[RETURN]], label [[BB]]
; CHECK: return:
; CHECK-NEXT: ret void
;
entry:
%0 = getelementptr inbounds double, ptr %G, i64 1
store double 1.000000e+00, ptr %0, align 8
%1 = add i32 %N, -1
%2 = icmp sgt i32 %1, 1
br i1 %2, label %bb.nph, label %return
bb.nph:
%tmp = sext i32 %1 to i64
%tmp7 = add i64 %tmp, -1
br label %bb
bb:
%indvar = phi i64 [ 0, %bb.nph ], [ %tmp9, %bb ]
%tmp8 = add i64 %indvar, 2
%scevgep = getelementptr double, ptr %G, i64 %tmp8
%tmp9 = add i64 %indvar, 1
%scevgep10 = getelementptr double, ptr %G, i64 %tmp9
%3 = load double, ptr %scevgep10, align 8
%4 = load double, ptr %scevgep, align 8
%5 = fadd double %3, %4
store double %5, ptr %scevgep, align 8
%exitcond = icmp eq i64 %tmp9, %tmp7
br i1 %exitcond, label %return, label %bb
; Should only be one load in the loop.
return:
ret void
}
;; Here the loaded address isn't available in 'block2' at all, requiring a new
;; GEP to be inserted into it.
define i32 @test8(ptr %p, ptr %q, ptr %Hack, i1 %C) {
; CHECK-LABEL: @test8(
; CHECK-NEXT: block1:
; CHECK-NEXT: br i1 [[C:%.*]], label [[BLOCK2:%.*]], label [[BLOCK3:%.*]]
; CHECK: block2:
; CHECK-NEXT: [[P3_PHI_TRANS_INSERT:%.*]] = getelementptr i32, ptr [[Q:%.*]], i32 1
; CHECK-NEXT: [[PRE_PRE:%.*]] = load i32, ptr [[P3_PHI_TRANS_INSERT]], align 4
; CHECK-NEXT: br label [[BLOCK4:%.*]]
; CHECK: block3:
; CHECK-NEXT: [[A:%.*]] = getelementptr i32, ptr [[P:%.*]], i32 1
; CHECK-NEXT: store i32 0, ptr [[A]], align 4
; CHECK-NEXT: br label [[BLOCK4]]
; CHECK: block4:
; CHECK-NEXT: [[PRE:%.*]] = phi i32 [ 0, [[BLOCK3]] ], [ [[PRE_PRE]], [[BLOCK2]] ]
; CHECK-NEXT: [[P2:%.*]] = phi ptr [ [[P]], [[BLOCK3]] ], [ [[Q]], [[BLOCK2]] ]
; CHECK-NEXT: [[P3:%.*]] = getelementptr i32, ptr [[P2]], i32 1
; CHECK-NEXT: ret i32 [[PRE]]
;
block1:
br i1 %C, label %block2, label %block3
block2:
br label %block4
block3:
%A = getelementptr i32, ptr %p, i32 1
store i32 0, ptr %A
br label %block4
block4:
%P2 = phi ptr [%p, %block3], [%q, %block2]
%P3 = getelementptr i32, ptr %P2, i32 1
%PRE = load i32, ptr %P3
ret i32 %PRE
}
;void test9(int N, ptr G) {
; long j;
; for (j = 1; j < N - 1; j++)
; G[j+1] = G[j] + G[j+1];
;}
; This requires phi translation of the adds.
define void @test9(i32 %N, ptr nocapture %G) nounwind ssp {
; CHECK-LABEL: @test9(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 1
; CHECK-NEXT: br i1 [[TMP1]], label [[BB_NPH:%.*]], label [[RETURN:%.*]]
; CHECK: bb.nph:
; CHECK-NEXT: [[TMP:%.*]] = sext i32 [[TMP0]] to i64
; CHECK-NEXT: [[TMP7:%.*]] = add i64 [[TMP]], -1
; CHECK-NEXT: [[SCEVGEP10_PHI_TRANS_INSERT:%.*]] = getelementptr double, ptr [[G:%.*]], i64 1
; CHECK-NEXT: [[DOTPRE:%.*]] = load double, ptr [[SCEVGEP10_PHI_TRANS_INSERT]], align 8
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[TMP2:%.*]] = phi double [ [[DOTPRE]], [[BB_NPH]] ], [ [[TMP4:%.*]], [[BB]] ]
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, [[BB_NPH]] ], [ [[TMP9:%.*]], [[BB]] ]
; CHECK-NEXT: [[TMP8:%.*]] = add i64 [[INDVAR]], 2
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP8]]
; CHECK-NEXT: [[TMP9]] = add i64 [[INDVAR]], 1
; CHECK-NEXT: [[SCEVGEP10:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP9]]
; CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[TMP4]] = fadd double [[TMP2]], [[TMP3]]
; CHECK-NEXT: store double [[TMP4]], ptr [[SCEVGEP]], align 8
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP9]], [[TMP7]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[RETURN]], label [[BB]]
; CHECK: return:
; CHECK-NEXT: ret void
;
entry:
add i32 0, 0
%1 = add i32 %N, -1
%2 = icmp sgt i32 %1, 1
br i1 %2, label %bb.nph, label %return
bb.nph:
%tmp = sext i32 %1 to i64
%tmp7 = add i64 %tmp, -1
br label %bb
bb:
%indvar = phi i64 [ 0, %bb.nph ], [ %tmp9, %bb ]
%tmp8 = add i64 %indvar, 2
%scevgep = getelementptr double, ptr %G, i64 %tmp8
%tmp9 = add i64 %indvar, 1
%scevgep10 = getelementptr double, ptr %G, i64 %tmp9
%3 = load double, ptr %scevgep10, align 8
%4 = load double, ptr %scevgep, align 8
%5 = fadd double %3, %4
store double %5, ptr %scevgep, align 8
%exitcond = icmp eq i64 %tmp9, %tmp7
br i1 %exitcond, label %return, label %bb
; Should only be one load in the loop.
return:
ret void
}
;void test10(int N, ptr G) {
; long j;
; for (j = 1; j < N - 1; j++)
; G[j] = G[j] + G[j+1] + G[j-1];
;}
; PR5501
define void @test10(i32 %N, ptr nocapture %G) nounwind ssp {
; CHECK-LABEL: @test10(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N:%.*]], -1
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 1
; CHECK-NEXT: br i1 [[TMP1]], label [[BB_NPH:%.*]], label [[RETURN:%.*]]
; CHECK: bb.nph:
; CHECK-NEXT: [[TMP:%.*]] = sext i32 [[TMP0]] to i64
; CHECK-NEXT: [[TMP8:%.*]] = add i64 [[TMP]], -1
; CHECK-NEXT: [[SCEVGEP12_PHI_TRANS_INSERT:%.*]] = getelementptr double, ptr [[G:%.*]], i64 1
; CHECK-NEXT: [[DOTPRE:%.*]] = load double, ptr [[SCEVGEP12_PHI_TRANS_INSERT]], align 8
; CHECK-NEXT: [[DOTPRE1:%.*]] = load double, ptr [[G]], align 8
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: [[TMP2:%.*]] = phi double [ [[DOTPRE1]], [[BB_NPH]] ], [ [[TMP6:%.*]], [[BB]] ]
; CHECK-NEXT: [[TMP3:%.*]] = phi double [ [[DOTPRE]], [[BB_NPH]] ], [ [[TMP4:%.*]], [[BB]] ]
; CHECK-NEXT: [[INDVAR:%.*]] = phi i64 [ 0, [[BB_NPH]] ], [ [[TMP11:%.*]], [[BB]] ]
; CHECK-NEXT: [[SCEVGEP:%.*]] = getelementptr double, ptr [[G]], i64 [[INDVAR]]
; CHECK-NEXT: [[TMP9:%.*]] = add i64 [[INDVAR]], 2
; CHECK-NEXT: [[SCEVGEP10:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP9]]
; CHECK-NEXT: [[TMP11]] = add i64 [[INDVAR]], 1
; CHECK-NEXT: [[SCEVGEP12:%.*]] = getelementptr double, ptr [[G]], i64 [[TMP11]]
; CHECK-NEXT: [[TMP4]] = load double, ptr [[SCEVGEP10]], align 8
; CHECK-NEXT: [[TMP5:%.*]] = fadd double [[TMP3]], [[TMP4]]
; CHECK-NEXT: [[TMP6]] = fadd double [[TMP5]], [[TMP2]]
; CHECK-NEXT: store double [[TMP6]], ptr [[SCEVGEP12]], align 8
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i64 [[TMP11]], [[TMP8]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[RETURN]], label [[BB]]
; CHECK: return:
; CHECK-NEXT: ret void
;
entry:
%0 = add i32 %N, -1
%1 = icmp sgt i32 %0, 1
br i1 %1, label %bb.nph, label %return
bb.nph:
%tmp = sext i32 %0 to i64
%tmp8 = add i64 %tmp, -1
br label %bb
bb:
%indvar = phi i64 [ 0, %bb.nph ], [ %tmp11, %bb ]
%scevgep = getelementptr double, ptr %G, i64 %indvar
%tmp9 = add i64 %indvar, 2
%scevgep10 = getelementptr double, ptr %G, i64 %tmp9
%tmp11 = add i64 %indvar, 1
%scevgep12 = getelementptr double, ptr %G, i64 %tmp11
%2 = load double, ptr %scevgep12, align 8
%3 = load double, ptr %scevgep10, align 8
%4 = fadd double %2, %3
%5 = load double, ptr %scevgep, align 8
%6 = fadd double %4, %5
store double %6, ptr %scevgep12, align 8
%exitcond = icmp eq i64 %tmp11, %tmp8
br i1 %exitcond, label %return, label %bb
; Should only be one load in the loop.
return:
ret void
}
; Test critical edge splitting.
define i32 @test11(ptr %p, i1 %C, i32 %N) {
; CHECK-LABEL: @test11(
; CHECK-NEXT: block1:
; CHECK-NEXT: br i1 [[C:%.*]], label [[BLOCK2:%.*]], label [[BLOCK3:%.*]]
; CHECK: block2:
; CHECK-NEXT: [[COND:%.*]] = icmp sgt i32 [[N:%.*]], 1
; CHECK-NEXT: br i1 [[COND]], label [[BLOCK2_BLOCK4_CRIT_EDGE:%.*]], label [[BLOCK5:%.*]]
; CHECK: block2.block4_crit_edge:
; CHECK-NEXT: [[PRE_PRE:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[BLOCK4:%.*]]
; CHECK: block3:
; CHECK-NEXT: store i32 0, ptr [[P]], align 4
; CHECK-NEXT: br label [[BLOCK4]]
; CHECK: block4:
; CHECK-NEXT: [[PRE:%.*]] = phi i32 [ [[PRE_PRE]], [[BLOCK2_BLOCK4_CRIT_EDGE]] ], [ 0, [[BLOCK3]] ]
; CHECK-NEXT: br label [[BLOCK5]]
; CHECK: block5:
; CHECK-NEXT: [[RET:%.*]] = phi i32 [ 0, [[BLOCK2]] ], [ [[PRE]], [[BLOCK4]] ]
; CHECK-NEXT: ret i32 [[RET]]
;
block1:
br i1 %C, label %block2, label %block3
block2:
%cond = icmp sgt i32 %N, 1
br i1 %cond, label %block4, label %block5
block3:
store i32 0, ptr %p
br label %block4
block4:
%PRE = load i32, ptr %p
br label %block5
block5:
%ret = phi i32 [ 0, %block2 ], [ %PRE, %block4 ]
ret i32 %ret
}
declare void @f()
declare void @g(i32)
declare i32 @__CxxFrameHandler3(...)
; Test that loads aren't PRE'd into EH pads.
define void @test12(ptr %p) personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @test12(
; CHECK-NEXT: block1:
; CHECK-NEXT: invoke void @f()
; CHECK-NEXT: to label [[BLOCK2:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK: block2:
; CHECK-NEXT: invoke void @f()
; CHECK-NEXT: to label [[BLOCK3:%.*]] unwind label [[CLEANUP:%.*]]
; CHECK: block3:
; CHECK-NEXT: ret void
; CHECK: catch.dispatch:
; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind label [[CLEANUP2:%.*]]
; CHECK: catch:
; CHECK-NEXT: [[C:%.*]] = catchpad within [[CS1]] []
; CHECK-NEXT: catchret from [[C]] to label [[BLOCK2]]
; CHECK: cleanup:
; CHECK-NEXT: [[C1:%.*]] = cleanuppad within none []
; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
; CHECK-NEXT: cleanupret from [[C1]] unwind label [[CLEANUP2]]
; CHECK: cleanup2:
; CHECK-NEXT: [[C2:%.*]] = cleanuppad within none []
; CHECK-NEXT: [[NOTPRE:%.*]] = load i32, ptr [[P]], align 4
; CHECK-NEXT: call void @g(i32 [[NOTPRE]])
; CHECK-NEXT: cleanupret from [[C2]] unwind to caller
;
block1:
invoke void @f()
to label %block2 unwind label %catch.dispatch
block2:
invoke void @f()
to label %block3 unwind label %cleanup
block3:
ret void
catch.dispatch:
%cs1 = catchswitch within none [label %catch] unwind label %cleanup2
catch:
%c = catchpad within %cs1 []
catchret from %c to label %block2
cleanup:
%c1 = cleanuppad within none []
store i32 0, ptr %p
cleanupret from %c1 unwind label %cleanup2
cleanup2:
%c2 = cleanuppad within none []
%NOTPRE = load i32, ptr %p
call void @g(i32 %NOTPRE)
cleanupret from %c2 unwind to caller
}
; Don't PRE load across potentially throwing calls.
define i32 @test13(ptr noalias nocapture readonly %x, ptr noalias nocapture %r, i32 %a) {
; CHECK-LABEL: @test13(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[A:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[UU:%.*]] = load i32, ptr [[X:%.*]], align 4
; CHECK-NEXT: store i32 [[UU]], ptr [[R:%.*]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: call void @f()
; CHECK-NEXT: [[VV:%.*]] = load i32, ptr [[X]], align 4
; CHECK-NEXT: ret i32 [[VV]]
;
entry:
%tobool = icmp eq i32 %a, 0
br i1 %tobool, label %if.end, label %if.then
if.then:
%uu = load i32, ptr %x, align 4
store i32 %uu, ptr %r, align 4
br label %if.end
if.end:
call void @f()
%vv = load i32, ptr %x, align 4
ret i32 %vv
}
; Same as test13, but now the blocking function is not immediately in load's
; block.
define i32 @test14(ptr noalias nocapture readonly %x, ptr noalias nocapture %r, i32 %a) {
; CHECK-LABEL: @test14(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[A:%.*]], 0
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[UU:%.*]] = load i32, ptr [[X:%.*]], align 4
; CHECK-NEXT: store i32 [[UU]], ptr [[R:%.*]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: call void @f()
; CHECK-NEXT: [[VV:%.*]] = load i32, ptr [[X]], align 4
; CHECK-NEXT: ret i32 [[VV]]
;
entry:
%tobool = icmp eq i32 %a, 0
br i1 %tobool, label %if.end, label %if.then
if.then:
%uu = load i32, ptr %x, align 4
store i32 %uu, ptr %r, align 4
br label %if.end
if.end:
call void @f()
br label %follow_1
follow_1:
br label %follow_2
follow_2:
%vv = load i32, ptr %x, align 4
ret i32 %vv
}
; Same as test13, but %x here is dereferenceable. A pointer that is
; dereferenceable can be loaded from speculatively without a risk of trapping.
; Since it is OK to speculate, PRE is allowed.
define i32 @test15(ptr noalias nocapture readonly dereferenceable(8) align 4 %x, ptr noalias nocapture %r, i32 %a) nofree nosync {
; CHECK-LABEL: @test15(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[A:%.*]], 0
; CHECK-NEXT: [[VV_PRE:%.*]] = load i32, ptr [[X:%.*]], align 4
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: store i32 [[VV_PRE]], ptr [[R:%.*]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: call void @f()
; CHECK-NEXT: ret i32 [[VV_PRE]]
;
entry:
%tobool = icmp eq i32 %a, 0
br i1 %tobool, label %if.end, label %if.then
if.then:
%uu = load i32, ptr %x, align 4
store i32 %uu, ptr %r, align 4
br label %if.end
if.end:
call void @f()
%vv = load i32, ptr %x, align 4
ret i32 %vv
}
; Same as test14, but %x here is dereferenceable. A pointer that is
; dereferenceable can be loaded from speculatively without a risk of trapping.
; Since it is OK to speculate, PRE is allowed.
define i32 @test16(ptr noalias nocapture readonly dereferenceable(8) align 4 %x, ptr noalias nocapture %r, i32 %a) nofree nosync {
; CHECK-LABEL: @test16(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[A:%.*]], 0
; CHECK-NEXT: [[VV_PRE:%.*]] = load i32, ptr [[X:%.*]], align 4
; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: store i32 [[VV_PRE]], ptr [[R:%.*]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: call void @f()
; CHECK-NEXT: ret i32 [[VV_PRE]]
;
entry:
%tobool = icmp eq i32 %a, 0
br i1 %tobool, label %if.end, label %if.then
if.then:
%uu = load i32, ptr %x, align 4
store i32 %uu, ptr %r, align 4
br label %if.end
if.end:
call void @f()
br label %follow_1
follow_1:
br label %follow_2
follow_2:
%vv = load i32, ptr %x, align 4
ret i32 %vv
}
declare i1 @foo()
declare i1 @bar()
; %v3 is partially redundant, bb3 has multiple predecessors coming through
; critical edges. The other successors of those predecessors have same loads.
; We can move all loads into predecessors.
define void @test17(ptr %p1, ptr %p2, ptr %p3, ptr %p4)
; CHECK-LABEL: @test17(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P1:%.*]], align 8
; CHECK-NEXT: [[COND1:%.*]] = icmp sgt i64 [[V1]], 200
; CHECK-NEXT: br i1 [[COND1]], label [[BB200:%.*]], label [[BB1:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[COND2:%.*]] = icmp sgt i64 [[V1]], 100
; CHECK-NEXT: br i1 [[COND2]], label [[BB100:%.*]], label [[BB2:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[V2:%.*]] = add nsw i64 [[V1]], 1
; CHECK-NEXT: store i64 [[V2]], ptr [[P1]], align 8
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb3:
; CHECK-NEXT: [[V3:%.*]] = phi i64 [ [[V3_PRE:%.*]], [[BB200]] ], [ [[V3_PRE1:%.*]], [[BB100]] ], [ [[V2]], [[BB2]] ]
; CHECK-NEXT: store i64 [[V3]], ptr [[P2:%.*]], align 8
; CHECK-NEXT: ret void
; CHECK: bb100:
; CHECK-NEXT: [[COND3:%.*]] = call i1 @foo()
; CHECK-NEXT: [[V3_PRE1]] = load i64, ptr [[P1]], align 8
; CHECK-NEXT: br i1 [[COND3]], label [[BB3]], label [[BB101:%.*]]
; CHECK: bb101:
; CHECK-NEXT: store i64 [[V3_PRE1]], ptr [[P3:%.*]], align 8
; CHECK-NEXT: ret void
; CHECK: bb200:
; CHECK-NEXT: [[COND4:%.*]] = call i1 @bar()
; CHECK-NEXT: [[V3_PRE]] = load i64, ptr [[P1]], align 8
; CHECK-NEXT: br i1 [[COND4]], label [[BB3]], label [[BB201:%.*]]
; CHECK: bb201:
; CHECK-NEXT: store i64 [[V3_PRE]], ptr [[P4:%.*]], align 8
; CHECK-NEXT: ret void
;
{
entry:
%v1 = load i64, ptr %p1, align 8
%cond1 = icmp sgt i64 %v1, 200
br i1 %cond1, label %bb200, label %bb1
bb1:
%cond2 = icmp sgt i64 %v1, 100
br i1 %cond2, label %bb100, label %bb2
bb2:
%v2 = add nsw i64 %v1, 1
store i64 %v2, ptr %p1, align 8
br label %bb3
bb3:
%v3 = load i64, ptr %p1, align 8
store i64 %v3, ptr %p2, align 8
ret void
bb100:
%cond3 = call i1 @foo()
br i1 %cond3, label %bb3, label %bb101
bb101:
%v4 = load i64, ptr %p1, align 8
store i64 %v4, ptr %p3, align 8
ret void
bb200:
%cond4 = call i1 @bar()
br i1 %cond4, label %bb3, label %bb201
bb201:
%v5 = load i64, ptr %p1, align 8
store i64 %v5, ptr %p4, align 8
ret void
}
; The output value from %if.then block is %dec, not loaded %v1.
; So ValuesPerBlock[%if.then] should not be replaced when the load instruction
; is moved to %entry.
define void @test18(i1 %cond, ptr %p1, ptr %p2) {
; CHECK-LABEL: @test18(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[V2_PRE:%.*]] = load i16, ptr [[P1:%.*]], align 2
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[DEC:%.*]] = add i16 [[V2_PRE]], -1
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[V2:%.*]] = phi i16 [ [[DEC]], [[IF_THEN]] ], [ [[V2_PRE]], [[ENTRY:%.*]] ]
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %cond, label %if.end, label %if.then
if.then:
%v1 = load i16, ptr %p1
%dec = add i16 %v1, -1
store i16 %dec, ptr %p1
br label %if.end
if.end:
%v2 = load i16, ptr %p1
store i16 %v2, ptr %p2
ret void
}
; PRE of load instructions should not cross exception handling instructions.
define void @test19(i1 %cond, ptr %p1, ptr %p2)
; CHECK-LABEL: @test19(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND:%.*]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[V2:%.*]] = load i64, ptr [[P2:%.*]], align 8
; CHECK-NEXT: [[ADD:%.*]] = add i64 [[V2]], 1
; CHECK-NEXT: store i64 [[ADD]], ptr [[P1:%.*]], align 8
; CHECK-NEXT: br label [[END:%.*]]
; CHECK: else:
; CHECK-NEXT: invoke void @f()
; CHECK-NEXT: to label [[ELSE_END_CRIT_EDGE:%.*]] unwind label [[LPAD:%.*]]
; CHECK: else.end_crit_edge:
; CHECK-NEXT: [[V1_PRE:%.*]] = load i64, ptr [[P1]], align 8
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: [[V1:%.*]] = phi i64 [ [[V1_PRE]], [[ELSE_END_CRIT_EDGE]] ], [ [[ADD]], [[THEN]] ]
; CHECK-NEXT: [[AND:%.*]] = and i64 [[V1]], 100
; CHECK-NEXT: store i64 [[AND]], ptr [[P2]], align 8
; CHECK-NEXT: ret void
; CHECK: lpad:
; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: [[V3:%.*]] = load i64, ptr [[P1]], align 8
; CHECK-NEXT: [[OR:%.*]] = or i64 [[V3]], 200
; CHECK-NEXT: store i64 [[OR]], ptr [[P1]], align 8
; CHECK-NEXT: resume { ptr, i32 } [[LP]]
;
personality ptr @__CxxFrameHandler3 {
entry:
br i1 %cond, label %then, label %else
then:
%v2 = load i64, ptr %p2
%add = add i64 %v2, 1
store i64 %add, ptr %p1
br label %end
else:
invoke void @f()
to label %end unwind label %lpad
end:
%v1 = load i64, ptr %p1
%and = and i64 %v1, 100
store i64 %and, ptr %p2
ret void
lpad:
%lp = landingpad { ptr, i32 }
cleanup
%v3 = load i64, ptr %p1
%or = or i64 %v3, 200
store i64 %or, ptr %p1
resume { ptr, i32 } %lp
}
; A predecessor BB has both successors to the same BB, for simplicity we don't
; handle it, nothing should be changed.
define void @test20(i1 %cond, i1 %cond2, ptr %p1, ptr %p2) {
; CHECK-LABEL: @test20(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[V1:%.*]] = load i16, ptr [[P1:%.*]], align 2
; CHECK-NEXT: [[DEC:%.*]] = add i16 [[V1]], -1
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[IF_END]], label [[IF_END]]
; CHECK: if.end:
; CHECK-NEXT: [[V2:%.*]] = load i16, ptr [[P1]], align 2
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %cond, label %if.then, label %if.else
if.then:
%v1 = load i16, ptr %p1
%dec = add i16 %v1, -1
store i16 %dec, ptr %p1
br label %if.end
if.else:
br i1 %cond2, label %if.end, label %if.end
if.end:
%v2 = load i16, ptr %p1
store i16 %v2, ptr %p2
ret void
}
; More edges from the same BB to LoadBB. Don't change anything.
define void @test21(i1 %cond, i32 %code, ptr %p1, ptr %p2) {
; CHECK-LABEL: @test21(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[V1:%.*]] = load i16, ptr [[P1:%.*]], align 2
; CHECK-NEXT: [[DEC:%.*]] = add i16 [[V1]], -1
; CHECK-NEXT: store i16 [[DEC]], ptr [[P1]], align 2
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.else:
; CHECK-NEXT: switch i32 [[CODE:%.*]], label [[IF_END]] [
; CHECK-NEXT: i32 1, label [[IF_END]]
; CHECK-NEXT: i32 2, label [[IF_END]]
; CHECK-NEXT: i32 3, label [[IF_END]]
; CHECK-NEXT: ]
; CHECK: if.end:
; CHECK-NEXT: [[V2:%.*]] = load i16, ptr [[P1]], align 2
; CHECK-NEXT: store i16 [[V2]], ptr [[P2:%.*]], align 2
; CHECK-NEXT: ret void
;
entry:
br i1 %cond, label %if.then, label %if.else
if.then:
%v1 = load i16, ptr %p1
%dec = add i16 %v1, -1
store i16 %dec, ptr %p1
br label %if.end
if.else:
switch i32 %code, label %if.end [
i32 1, label %if.end
i32 2, label %if.end
i32 3, label %if.end
]
if.end:
%v2 = load i16, ptr %p1
store i16 %v2, ptr %p2
ret void
}
; Call to function @maybethrow may cause exception, so the load of %v3 can't
; be hoisted to block %if.else.
define void @test22(i1 %cond, ptr %p1, ptr %p2) {
; CHECK-LABEL: @test22(
; CHECK-NEXT: entry:
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then:
; CHECK-NEXT: [[V1:%.*]] = load i64, ptr [[P1:%.*]], align 8
; CHECK-NEXT: [[DEC:%.*]] = add i64 [[V1]], -1
; CHECK-NEXT: store i64 [[DEC]], ptr [[P1]], align 8
; CHECK-NEXT: br label [[IF_END:%.*]]
; CHECK: if.end:
; CHECK-NEXT: [[V2:%.*]] = phi i64 [ [[V2_PRE:%.*]], [[IF_ELSE_IF_END_CRIT_EDGE:%.*]] ], [ [[DEC]], [[IF_THEN]] ]
; CHECK-NEXT: store i64 [[V2]], ptr [[P2:%.*]], align 8
; CHECK-NEXT: ret void
; CHECK: if.else:
; CHECK-NEXT: [[COND2:%.*]] = call i1 @foo()
; CHECK-NEXT: br i1 [[COND2]], label [[IF_ELSE_IF_END_CRIT_EDGE]], label [[EXIT:%.*]]
; CHECK: if.else.if.end_crit_edge:
; CHECK-NEXT: [[V2_PRE]] = load i64, ptr [[P1]], align 8
; CHECK-NEXT: br label [[IF_END]]
; CHECK: exit:
; CHECK-NEXT: [[_:%.*]] = call i1 @maybethrow()
; CHECK-NEXT: [[V3:%.*]] = load i64, ptr [[P1]], align 8
; CHECK-NEXT: store i64 [[V3]], ptr [[P2]], align 8
; CHECK-NEXT: ret void
;
entry:
br i1 %cond, label %if.then, label %if.else
if.then:
%v1 = load i64, ptr %p1
%dec = add i64 %v1, -1
store i64 %dec, ptr %p1
br label %if.end
if.end:
%v2 = load i64, ptr %p1
store i64 %v2, ptr %p2
ret void
if.else:
%cond2 = call i1 @foo()
br i1 %cond2, label %if.end, label %exit
exit:
%_ = call i1 @maybethrow()
%v3 = load i64, ptr %p1
store i64 %v3, ptr %p2
ret void
}
declare void @maybethrow() readnone
@B = external global i64, align 8
; When BB in ValuesPerBlock(BB, OldLoad) is not OldLoad->getParent(), it should
; also be replaced by ValuesPerBlock(BB, NewLoad). So we'll not use the deleted
; OldLoad in later PHI instruction.
define void @test23(i1 %cond1, i1 %cond2) {
; CHECK-LABEL: @test23(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[G:%.*]] = alloca i64, align 8
; CHECK-NEXT: [[VAL1_PRE:%.*]] = load i64, ptr @B, align 8
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[THEN:%.*]], label [[WRONG:%.*]]
; CHECK: then:
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[STORE:%.*]], label [[EXIT:%.*]]
; CHECK: store:
; CHECK-NEXT: store i64 [[VAL1_PRE]], ptr @B, align 8
; CHECK-NEXT: br label [[WRONG]]
; CHECK: wrong:
; CHECK-NEXT: store i64 [[VAL1_PRE]], ptr [[G]], align 8
; CHECK-NEXT: ret void
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%G = alloca i64, align 8
br i1 %cond2, label %then, label %wrong
then:
%val2 = load i64, ptr @B, align 8
br i1 %cond1, label %store, label %exit
store:
store i64 %val2, ptr @B, align 8
br label %wrong
wrong:
%val1 = load i64, ptr @B, align 8
store i64 %val1, ptr %G, align 8
ret void
exit:
ret void
}