Before elimination of mostly empty block it makes sense to remove dead PHI nodes. It open more opportunity for elimination plus eliminates dead code itself. It appeared that change results in failing many unit tests and some of them I've updated and for another one I disable this optimization. The pattern I observed in the tests is that there is a infinite loop without side effects. As a result after elimination of dead phi node all other related instruction are also removed and tests stops to check what it is expected. Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D158503
293 lines
12 KiB
LLVM
293 lines
12 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt -codegenprepare -S < %s | FileCheck %s
|
|
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
|
target triple = "x86_64-unknown-linux-gnu"
|
|
|
|
declare void @use(i32) local_unnamed_addr
|
|
declare void @useptr(ptr) local_unnamed_addr
|
|
|
|
; CHECK: @simple.targets = constant [2 x ptr] [ptr blockaddress(@simple, %bb0), ptr blockaddress(@simple, %bb1)], align 16
|
|
@simple.targets = constant [2 x ptr] [ptr blockaddress(@simple, %bb0), ptr blockaddress(@simple, %bb1)], align 16
|
|
|
|
; CHECK: @multi.targets = constant [2 x ptr] [ptr blockaddress(@multi, %bb0), ptr blockaddress(@multi, %bb1)], align 16
|
|
@multi.targets = constant [2 x ptr] [ptr blockaddress(@multi, %bb0), ptr blockaddress(@multi, %bb1)], align 16
|
|
|
|
; CHECK: @loop.targets = constant [2 x ptr] [ptr blockaddress(@loop, %bb0), ptr blockaddress(@loop, %bb1)], align 16
|
|
@loop.targets = constant [2 x ptr] [ptr blockaddress(@loop, %bb0), ptr blockaddress(@loop, %bb1)], align 16
|
|
|
|
; CHECK: @nophi.targets = constant [2 x ptr] [ptr blockaddress(@nophi, %bb0), ptr blockaddress(@nophi, %bb1)], align 16
|
|
@nophi.targets = constant [2 x ptr] [ptr blockaddress(@nophi, %bb0), ptr blockaddress(@nophi, %bb1)], align 16
|
|
|
|
; CHECK: @noncritical.targets = constant [2 x ptr] [ptr blockaddress(@noncritical, %bb0), ptr blockaddress(@noncritical, %bb1)], align 16
|
|
@noncritical.targets = constant [2 x ptr] [ptr blockaddress(@noncritical, %bb0), ptr blockaddress(@noncritical, %bb1)], align 16
|
|
|
|
; Check that we break the critical edge when an jump table has only one use.
|
|
define void @simple(ptr nocapture readonly %p) {
|
|
; CHECK-LABEL: @simple(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
|
|
; CHECK-NEXT: [[INITVAL:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: [[INITOP:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4
|
|
; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[BB0_CLONE:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[BB1_CLONE:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
|
|
; CHECK: .split:
|
|
; CHECK-NEXT: [[MERGE:%.*]] = phi ptr [ [[PTR:%.*]], [[BB0:%.*]] ], [ [[INCDEC_PTR]], [[BB0_CLONE]] ]
|
|
; CHECK-NEXT: [[MERGE2:%.*]] = phi i32 [ 0, [[BB0]] ], [ [[INITVAL]], [[BB0_CLONE]] ]
|
|
; CHECK-NEXT: tail call void @use(i32 [[MERGE2]])
|
|
; CHECK-NEXT: br label [[INDIRECTGOTO:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: br label [[DOTSPLIT3:%.*]]
|
|
; CHECK: .split3:
|
|
; CHECK-NEXT: [[MERGE5:%.*]] = phi ptr [ [[PTR]], [[BB1:%.*]] ], [ [[INCDEC_PTR]], [[BB1_CLONE]] ]
|
|
; CHECK-NEXT: [[MERGE7:%.*]] = phi i32 [ 1, [[BB1]] ], [ [[INITVAL]], [[BB1_CLONE]] ]
|
|
; CHECK-NEXT: tail call void @use(i32 [[MERGE7]])
|
|
; CHECK-NEXT: br label [[INDIRECTGOTO]]
|
|
; CHECK: indirectgoto:
|
|
; CHECK-NEXT: [[P_ADDR_SINK:%.*]] = phi ptr [ [[MERGE5]], [[DOTSPLIT3]] ], [ [[MERGE]], [[DOTSPLIT]] ]
|
|
; CHECK-NEXT: [[PTR]] = getelementptr inbounds i32, ptr [[P_ADDR_SINK]], i64 1
|
|
; CHECK-NEXT: [[NEWP:%.*]] = load i32, ptr [[P_ADDR_SINK]], align 4
|
|
; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[NEWP]] to i64
|
|
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr @simple.targets, i64 0, i64 [[IDX]]
|
|
; CHECK-NEXT: [[NEWOP:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8
|
|
; CHECK-NEXT: indirectbr ptr [[NEWOP]], [label [[BB0]], label %bb1]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: bb0.clone:
|
|
; CHECK-NEXT: br label [[DOTSPLIT]]
|
|
; CHECK: bb1.clone:
|
|
; CHECK-NEXT: br label [[DOTSPLIT3]]
|
|
;
|
|
entry:
|
|
%incdec.ptr = getelementptr inbounds i32, ptr %p, i64 1
|
|
%initval = load i32, ptr %p, align 4
|
|
%initop = load i32, ptr %incdec.ptr, align 4
|
|
switch i32 %initop, label %exit [
|
|
i32 0, label %bb0
|
|
i32 1, label %bb1
|
|
]
|
|
|
|
bb0:
|
|
%p.addr.0 = phi ptr [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ]
|
|
%opcode.0 = phi i32 [ %initval, %entry ], [ 0, %indirectgoto ]
|
|
tail call void @use(i32 %opcode.0)
|
|
br label %indirectgoto
|
|
|
|
bb1:
|
|
%p.addr.1 = phi ptr [ %incdec.ptr, %entry ], [ %ptr, %indirectgoto ]
|
|
%opcode.1 = phi i32 [ %initval, %entry ], [ 1, %indirectgoto ]
|
|
tail call void @use(i32 %opcode.1)
|
|
br label %indirectgoto
|
|
|
|
indirectgoto:
|
|
%p.addr.sink = phi ptr [ %p.addr.1, %bb1 ], [ %p.addr.0, %bb0 ]
|
|
%ptr = getelementptr inbounds i32, ptr %p.addr.sink, i64 1
|
|
%newp = load i32, ptr %p.addr.sink, align 4
|
|
%idx = sext i32 %newp to i64
|
|
%arrayidx = getelementptr inbounds [2 x ptr], ptr @simple.targets, i64 0, i64 %idx
|
|
%newop = load ptr, ptr %arrayidx, align 8
|
|
indirectbr ptr %newop, [label %bb0, label %bb1]
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Don't try to break critical edges when several indirectbr point to a single block
|
|
define void @multi(ptr nocapture readonly %p) {
|
|
; CHECK-LABEL: @multi(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
|
|
; CHECK-NEXT: [[INITVAL:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: [[INITOP:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4
|
|
; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[BB0:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[BB1:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: [[P_ADDR_0:%.*]] = phi ptr [ [[INCDEC_PTR]], [[ENTRY:%.*]] ], [ [[NEXT0:%.*]], [[BB0]] ], [ [[NEXT1:%.*]], [[BB1]] ]
|
|
; CHECK-NEXT: [[OPCODE_0:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[BB0]] ], [ 1, [[BB1]] ]
|
|
; CHECK-NEXT: tail call void @use(i32 [[OPCODE_0]])
|
|
; CHECK-NEXT: [[NEXT0]] = getelementptr inbounds i32, ptr [[P_ADDR_0]], i64 1
|
|
; CHECK-NEXT: [[NEWP0:%.*]] = load i32, ptr [[P_ADDR_0]], align 4
|
|
; CHECK-NEXT: [[IDX0:%.*]] = sext i32 [[NEWP0]] to i64
|
|
; CHECK-NEXT: [[ARRAYIDX0:%.*]] = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 [[IDX0]]
|
|
; CHECK-NEXT: [[NEWOP0:%.*]] = load ptr, ptr [[ARRAYIDX0]], align 8
|
|
; CHECK-NEXT: indirectbr ptr [[NEWOP0]], [label [[BB0]], label %bb1]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: [[P_ADDR_1:%.*]] = phi ptr [ [[INCDEC_PTR]], [[ENTRY]] ], [ [[NEXT0]], [[BB0]] ], [ [[NEXT1]], [[BB1]] ]
|
|
; CHECK-NEXT: [[OPCODE_1:%.*]] = phi i32 [ [[INITVAL]], [[ENTRY]] ], [ 0, [[BB0]] ], [ 1, [[BB1]] ]
|
|
; CHECK-NEXT: tail call void @use(i32 [[OPCODE_1]])
|
|
; CHECK-NEXT: [[NEXT1]] = getelementptr inbounds i32, ptr [[P_ADDR_1]], i64 1
|
|
; CHECK-NEXT: [[NEWP1:%.*]] = load i32, ptr [[P_ADDR_1]], align 4
|
|
; CHECK-NEXT: [[IDX1:%.*]] = sext i32 [[NEWP1]] to i64
|
|
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 [[IDX1]]
|
|
; CHECK-NEXT: [[NEWOP1:%.*]] = load ptr, ptr [[ARRAYIDX1]], align 8
|
|
; CHECK-NEXT: indirectbr ptr [[NEWOP1]], [label [[BB0]], label %bb1]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%incdec.ptr = getelementptr inbounds i32, ptr %p, i64 1
|
|
%initval = load i32, ptr %p, align 4
|
|
%initop = load i32, ptr %incdec.ptr, align 4
|
|
switch i32 %initop, label %exit [
|
|
i32 0, label %bb0
|
|
i32 1, label %bb1
|
|
]
|
|
|
|
bb0:
|
|
%p.addr.0 = phi ptr [ %incdec.ptr, %entry ], [ %next0, %bb0 ], [ %next1, %bb1 ]
|
|
%opcode.0 = phi i32 [ %initval, %entry ], [ 0, %bb0 ], [ 1, %bb1 ]
|
|
tail call void @use(i32 %opcode.0)
|
|
%next0 = getelementptr inbounds i32, ptr %p.addr.0, i64 1
|
|
%newp0 = load i32, ptr %p.addr.0, align 4
|
|
%idx0 = sext i32 %newp0 to i64
|
|
%arrayidx0 = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 %idx0
|
|
%newop0 = load ptr, ptr %arrayidx0, align 8
|
|
indirectbr ptr %newop0, [label %bb0, label %bb1]
|
|
|
|
bb1:
|
|
%p.addr.1 = phi ptr [ %incdec.ptr, %entry ], [ %next0, %bb0 ], [ %next1, %bb1 ]
|
|
%opcode.1 = phi i32 [ %initval, %entry ], [ 0, %bb0 ], [ 1, %bb1 ]
|
|
tail call void @use(i32 %opcode.1)
|
|
%next1 = getelementptr inbounds i32, ptr %p.addr.1, i64 1
|
|
%newp1 = load i32, ptr %p.addr.1, align 4
|
|
%idx1 = sext i32 %newp1 to i64
|
|
%arrayidx1 = getelementptr inbounds [2 x ptr], ptr @multi.targets, i64 0, i64 %idx1
|
|
%newop1 = load ptr, ptr %arrayidx1, align 8
|
|
indirectbr ptr %newop1, [label %bb0, label %bb1]
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Make sure we do the right thing for cases where the indirectbr branches to
|
|
; the block it terminates.
|
|
define i64 @loop(ptr nocapture readonly %p) {
|
|
; CHECK-LABEL: @loop(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br label [[DOTSPLIT:%.*]]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: br label [[DOTSPLIT]]
|
|
; CHECK: .split:
|
|
; CHECK-NEXT: [[MERGE:%.*]] = phi i64 [ [[I_NEXT:%.*]], [[BB0:%.*]] ], [ 0, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 [[MERGE]]
|
|
; CHECK-NEXT: store i64 [[MERGE]], ptr [[TMP0]], align 4
|
|
; CHECK-NEXT: [[I_NEXT]] = add nuw nsw i64 [[MERGE]], 1
|
|
; CHECK-NEXT: [[IDX:%.*]] = srem i64 [[MERGE]], 2
|
|
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr @loop.targets, i64 0, i64 [[IDX]]
|
|
; CHECK-NEXT: [[TARGET:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8
|
|
; CHECK-NEXT: indirectbr ptr [[TARGET]], [label [[BB0]], label %bb1]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: ret i64 [[I_NEXT]]
|
|
;
|
|
entry:
|
|
br label %bb0
|
|
|
|
bb0:
|
|
%i = phi i64 [ %i.next, %bb0 ], [ 0, %entry ]
|
|
%tmp0 = getelementptr inbounds i64, ptr %p, i64 %i
|
|
store i64 %i, ptr %tmp0, align 4
|
|
%i.next = add nuw nsw i64 %i, 1
|
|
%idx = srem i64 %i, 2
|
|
%arrayidx = getelementptr inbounds [2 x ptr], ptr @loop.targets, i64 0, i64 %idx
|
|
%target = load ptr, ptr %arrayidx, align 8
|
|
indirectbr ptr %target, [label %bb0, label %bb1]
|
|
|
|
bb1:
|
|
ret i64 %i.next
|
|
}
|
|
|
|
; Don't do anything for cases that contain no phis.
|
|
define void @nophi(ptr %p) {
|
|
; CHECK-LABEL: @nophi(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[INCDEC_PTR:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1
|
|
; CHECK-NEXT: [[INITOP:%.*]] = load i32, ptr [[INCDEC_PTR]], align 4
|
|
; CHECK-NEXT: switch i32 [[INITOP]], label [[EXIT:%.*]] [
|
|
; CHECK-NEXT: i32 0, label [[BB0:%.*]]
|
|
; CHECK-NEXT: i32 1, label [[BB1:%.*]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: tail call void @use(i32 0)
|
|
; CHECK-NEXT: br label [[INDIRECTGOTO:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: tail call void @use(i32 1)
|
|
; CHECK-NEXT: br label [[INDIRECTGOTO]]
|
|
; CHECK: indirectgoto:
|
|
; CHECK-NEXT: [[SUNKADDR:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4
|
|
; CHECK-NEXT: [[NEWP:%.*]] = load i32, ptr [[SUNKADDR]], align 4
|
|
; CHECK-NEXT: [[IDX:%.*]] = sext i32 [[NEWP]] to i64
|
|
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x ptr], ptr @nophi.targets, i64 0, i64 [[IDX]]
|
|
; CHECK-NEXT: [[NEWOP:%.*]] = load ptr, ptr [[ARRAYIDX]], align 8
|
|
; CHECK-NEXT: indirectbr ptr [[NEWOP]], [label [[BB0]], label %bb1]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%incdec.ptr = getelementptr inbounds i32, ptr %p, i64 1
|
|
%initop = load i32, ptr %incdec.ptr, align 4
|
|
switch i32 %initop, label %exit [
|
|
i32 0, label %bb0
|
|
i32 1, label %bb1
|
|
]
|
|
|
|
bb0:
|
|
tail call void @use(i32 0) br label %indirectgoto
|
|
|
|
bb1:
|
|
tail call void @use(i32 1)
|
|
br label %indirectgoto
|
|
|
|
indirectgoto:
|
|
%newp = load i32, ptr %incdec.ptr, align 4
|
|
%idx = sext i32 %newp to i64
|
|
%arrayidx = getelementptr inbounds [2 x ptr], ptr @nophi.targets, i64 0, i64 %idx
|
|
%newop = load ptr, ptr %arrayidx, align 8
|
|
indirectbr ptr %newop, [label %bb0, label %bb1]
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; Don't do anything if the edge isn't critical.
|
|
define i32 @noncritical(i32 %k, ptr %p)
|
|
; CHECK-LABEL: @noncritical(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[D:%.*]] = add i32 [[K:%.*]], 1
|
|
; CHECK-NEXT: indirectbr ptr [[P:%.*]], [label [[BB0:%.*]], label %bb1]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: [[R0:%.*]] = sub i32 [[K]], [[D]]
|
|
; CHECK-NEXT: br label [[EXIT:%.*]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: [[R1:%.*]] = sub i32 [[D]], [[K]]
|
|
; CHECK-NEXT: br label [[EXIT]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: [[V:%.*]] = phi i32 [ [[R0]], [[BB0]] ], [ [[R1]], [[BB1:%.*]] ]
|
|
; CHECK-NEXT: ret i32 [[V]]
|
|
;
|
|
{
|
|
entry:
|
|
%d = add i32 %k, 1
|
|
indirectbr ptr %p, [label %bb0, label %bb1]
|
|
|
|
bb0:
|
|
%v00 = phi i32 [%k, %entry]
|
|
%v01 = phi i32 [%d, %entry]
|
|
%r0 = sub i32 %v00, %v01
|
|
br label %exit
|
|
|
|
bb1:
|
|
%v10 = phi i32 [%d, %entry]
|
|
%v11 = phi i32 [%k, %entry]
|
|
%r1 = sub i32 %v10, %v11
|
|
br label %exit
|
|
|
|
exit:
|
|
%v = phi i32 [%r0, %bb0], [%r1, %bb1]
|
|
ret i32 %v
|
|
}
|