I strongly believe we need some variant of this. The main problem is e.g. that the glibc's assert has 4 parameters, but the profitability check is only okay with one extra phi node, so D116692 doesn't even trigger on most of the expected cases. While that restriction probably makes sense in normal code, if we are about to run off of a cliff (into an `unreachable`), this successor block is unlikely so the cost to setup these PHI nodes should not be on the hotpath, and shouldn't matter performance-wise. Likewise, we don't sink if there are unconditional predecessors UNLESS we'd sink at least one non-speculatable instruction, which is a performance workaround, but if we are about to run into `unreachable`, it shouldn't matter. Note that we only allow the case where there are at most unconditiona branches on the way to the unreachable block. Differential Revision: https://reviews.llvm.org/D117045
125 lines
4.0 KiB
LLVM
125 lines
4.0 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -simplifycfg -simplifycfg-require-and-preserve-domtree=1 -sink-common-insts -S | FileCheck %s
|
|
; RUN: opt < %s -passes='simplifycfg<sink-common-insts>' -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"
|
|
target triple = "x86_64-pc-linux-gnu"
|
|
|
|
; `unreachable` block has conditional predecessors,
|
|
; but everything we'd sink from unconditional predecessors are speculatable.
|
|
; Also, too many phi's would normally be needed.
|
|
define void @t0(i4 %cond, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) {
|
|
; CHECK-LABEL: @t0(
|
|
; CHECK-NEXT: switch i4 [[COND:%.*]], label [[END:%.*]] [
|
|
; CHECK-NEXT: i4 0, label [[END_SINK_SPLIT:%.*]]
|
|
; CHECK-NEXT: i4 -1, label [[END_SINK_SPLIT]]
|
|
; CHECK-NEXT: i4 1, label [[BB1:%.*]]
|
|
; CHECK-NEXT: i4 -2, label [[BB1]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: br label [[END_SINK_SPLIT]]
|
|
; CHECK: end.sink.split:
|
|
; CHECK-NEXT: [[H_SINK:%.*]] = phi i32 [ [[H:%.*]], [[BB1]] ], [ [[E:%.*]], [[TMP0:%.*]] ], [ [[E]], [[TMP0]] ]
|
|
; CHECK-NEXT: [[G_SINK:%.*]] = phi i32 [ [[G:%.*]], [[BB1]] ], [ [[D:%.*]], [[TMP0]] ], [ [[D]], [[TMP0]] ]
|
|
; CHECK-NEXT: [[V4:%.*]] = add i32 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[V5:%.*]] = add i32 [[V4]], [[C:%.*]]
|
|
; CHECK-NEXT: [[V6:%.*]] = add i32 [[G_SINK]], [[H_SINK]]
|
|
; CHECK-NEXT: [[R7:%.*]] = add i32 [[V5]], [[V6]]
|
|
; CHECK-NEXT: call void @use32(i32 [[R7]])
|
|
; CHECK-NEXT: unreachable
|
|
; CHECK: end:
|
|
; CHECK-NEXT: unreachable
|
|
;
|
|
switch i4 %cond, label %end [
|
|
i4 0, label %bb0
|
|
i4 -1, label %bb0
|
|
i4 1, label %bb1
|
|
i4 -2, label %bb1
|
|
]
|
|
|
|
bb0:
|
|
%v0 = add i32 %a, %b
|
|
%v1 = add i32 %v0, %c
|
|
%v2 = add i32 %d, %e
|
|
%r3 = add i32 %v1, %v2
|
|
call void @use32(i32 %r3)
|
|
br label %end
|
|
|
|
bb1:
|
|
%v4 = add i32 %a, %b
|
|
%v5 = add i32 %v4, %c
|
|
%v6 = add i32 %g, %h
|
|
%r7 = add i32 %v5, %v6
|
|
call void @use32(i32 %r7)
|
|
br label %end
|
|
|
|
end:
|
|
unreachable
|
|
}
|
|
|
|
; Same, as @t0, but there's also a loop.
|
|
define void @t1(i4 %cond, i1 %cond.loop, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f, i32 %g, i32 %h) {
|
|
; CHECK-LABEL: @t1(
|
|
; CHECK-NEXT: switch i4 [[COND:%.*]], label [[END_EARLY:%.*]] [
|
|
; CHECK-NEXT: i4 0, label [[BB0:%.*]]
|
|
; CHECK-NEXT: i4 -1, label [[BB0]]
|
|
; CHECK-NEXT: i4 1, label [[BB1:%.*]]
|
|
; CHECK-NEXT: i4 -2, label [[BB1]]
|
|
; CHECK-NEXT: ]
|
|
; CHECK: bb0:
|
|
; CHECK-NEXT: [[V0:%.*]] = add i32 [[A:%.*]], [[B:%.*]]
|
|
; CHECK-NEXT: [[V1:%.*]] = add i32 [[V0]], [[C:%.*]]
|
|
; CHECK-NEXT: [[V2:%.*]] = add i32 [[D:%.*]], [[E:%.*]]
|
|
; CHECK-NEXT: [[R3:%.*]] = add i32 [[V1]], [[V2]]
|
|
; CHECK-NEXT: call void @use32(i32 [[R3]])
|
|
; CHECK-NEXT: br label [[END_EARLY]]
|
|
; CHECK: bb1:
|
|
; CHECK-NEXT: [[V4:%.*]] = add i32 [[A]], [[B]]
|
|
; CHECK-NEXT: [[V5:%.*]] = add i32 [[V4]], [[C]]
|
|
; CHECK-NEXT: [[V6:%.*]] = add i32 [[G:%.*]], [[H:%.*]]
|
|
; CHECK-NEXT: [[R7:%.*]] = add i32 [[V5]], [[V6]]
|
|
; CHECK-NEXT: call void @use32(i32 [[R7]])
|
|
; CHECK-NEXT: br label [[END_EARLY]]
|
|
; CHECK: end.early:
|
|
; CHECK-NEXT: call void @sideeffect()
|
|
; CHECK-NEXT: br i1 [[COND_LOOP:%.*]], label [[END_EARLY]], label [[END:%.*]]
|
|
; CHECK: end:
|
|
; CHECK-NEXT: call void @sideeffect()
|
|
; CHECK-NEXT: unreachable
|
|
;
|
|
switch i4 %cond, label %end.early [
|
|
i4 0, label %bb0
|
|
i4 -1, label %bb0
|
|
i4 1, label %bb1
|
|
i4 -2, label %bb1
|
|
]
|
|
|
|
bb0:
|
|
%v0 = add i32 %a, %b
|
|
%v1 = add i32 %v0, %c
|
|
%v2 = add i32 %d, %e
|
|
%r3 = add i32 %v1, %v2
|
|
call void @use32(i32 %r3)
|
|
br label %end.early
|
|
|
|
bb1:
|
|
%v4 = add i32 %a, %b
|
|
%v5 = add i32 %v4, %c
|
|
%v6 = add i32 %g, %h
|
|
%r7 = add i32 %v5, %v6
|
|
call void @use32(i32 %r7)
|
|
br label %end.early
|
|
|
|
end.early:
|
|
call void @sideeffect()
|
|
br i1 %cond.loop, label %end.early, label %end
|
|
|
|
end:
|
|
call void @sideeffect()
|
|
unreachable
|
|
}
|
|
|
|
declare void @use32(i32) speculatable
|
|
|
|
declare void @sideeffect()
|