Skip to content

Reland "[SimplifyCFG] Hoist common instructions on switch" #67077

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
371 changes: 225 additions & 146 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AArch64/patchable-function-entry-bti.ll
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ entry:
i64 4, label %sw.bb4
]
sw.bb0:
call void asm sideeffect "", ""()
call void asm sideeffect "nop", ""()
ret void
sw.bb1:
call void asm sideeffect "", ""()
Expand Down
16 changes: 2 additions & 14 deletions llvm/test/Transforms/SimplifyCFG/HoistCode.ll
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,9 @@ F: ; preds = %0

define void @foo_switch(i64 %C, ptr %P) {
; CHECK-LABEL: @foo_switch(
; CHECK-NEXT: switch i64 [[C:%.*]], label [[BB0:%.*]] [
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: bb0:
; CHECK-NEXT: common.ret:
; CHECK-NEXT: store i32 7, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
; CHECK: bb1:
; CHECK-NEXT: store i32 7, ptr [[P]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: bb2:
; CHECK-NEXT: store i32 7, ptr [[P]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK-NEXT: ret void
;
switch i64 %C, label %bb0 [
i64 1, label %bb1
Expand Down
105 changes: 84 additions & 21 deletions llvm/test/Transforms/SimplifyCFG/hoist-common-code-with-unreachable.ll
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,8 @@
define i1 @common_instr_with_unreachable(i64 %a, i64 %b, i64 %c) {
; CHECK-LABEL: @common_instr_with_unreachable(
; CHECK-NEXT: start:
; CHECK-NEXT: switch i64 [[A:%.*]], label [[UNREACHABLE:%.*]] [
; CHECK-NEXT: i64 0, label [[BB0:%.*]]
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: unreachable:
; CHECK-NEXT: unreachable
; CHECK: bb0:
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
; CHECK-NEXT: br label [[EXIT]]
; CHECK: bb2:
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
; CHECK-NEXT: ret i1 [[RESULT]]
; CHECK-NEXT: ret i1 [[TMP0]]
;
start:
switch i64 %a, label %unreachable [
Expand Down Expand Up @@ -54,46 +37,126 @@ exit: ; preds = %bb2, %bb1, %bb0
define i1 @common_instr_with_unreachable_2(i64 %a, i64 %b, i64 %c) {
; CHECK-LABEL: @common_instr_with_unreachable_2(
; CHECK-NEXT: start:
; CHECK-NEXT: switch i64 [[A:%.*]], label [[BB1:%.*]] [
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: ret i1 [[TMP0]]
;
start:
switch i64 %a, label %bb1 [
i64 0, label %bb0
i64 1, label %unreachable
i64 2, label %bb2
]

unreachable:
unreachable

bb0: ; preds = %start
%0 = icmp eq i64 %b, %c
br label %exit

bb1: ; preds = %start
%1 = icmp eq i64 %b, %c
br label %exit

bb2: ; preds = %start
%2 = icmp eq i64 %b, %c
br label %exit

exit: ; preds = %bb2, %bb1, %bb0
%result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
ret i1 %result
}

define i1 @not_only_unreachable(i64 %a, i64 %b, i64 %c) {
; CHECK-LABEL: @not_only_unreachable(
; CHECK-NEXT: start:
; CHECK-NEXT: switch i64 [[A:%.*]], label [[UNREACHABLE:%.*]] [
; CHECK-NEXT: i64 0, label [[BB0:%.*]]
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: unreachable:
; CHECK-NEXT: call void @no_return()
; CHECK-NEXT: unreachable
; CHECK: bb0:
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT]]
; CHECK: bb2:
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
; CHECK-NEXT: ret i1 [[RESULT]]
;
start:
switch i64 %a, label %bb1 [
switch i64 %a, label %unreachable [
i64 0, label %bb0
i64 1, label %unreachable
i64 1, label %bb1
i64 2, label %bb2
]

unreachable:
call void @no_return()
unreachable

bb0: ; preds = %start
%0 = icmp eq i64 %b, %c
call void @foo()
br label %exit

bb1: ; preds = %start
%1 = icmp eq i64 %b, %c
call void @foo()
br label %exit

bb2: ; preds = %start
%2 = icmp eq i64 %b, %c
call void @foo()
br label %exit

exit: ; preds = %bb2, %bb1, %bb0
%result = phi i1 [ %0, %bb0 ], [ %1, %bb1 ], [ %2, %bb2 ]
ret i1 %result
}

; If we can hoist a musttail call,
; we can and have to hoist subsequent bitcast and ret instructions.
define ptr @switch_musttail_call(ptr %arg) {
; CHECK-LABEL: @switch_musttail_call(
; CHECK-NEXT: bb:
; CHECK-NEXT: [[P0:%.*]] = musttail call ptr @musttail_call(ptr [[ARG:%.*]])
; CHECK-NEXT: ret ptr [[P0]]
;
bb:
%load = load i16, ptr %arg, align 2
switch i16 %load, label %unreachable [
i16 0, label %bb0
i16 1, label %bb1
i16 2, label %bb2
]

unreachable:
unreachable

bb0:
%p0 = musttail call ptr @musttail_call(ptr %arg)
ret ptr %p0

bb1:
%p1 = musttail call ptr @musttail_call(ptr %arg)
ret ptr %p1

bb2:
%p2 = musttail call ptr @musttail_call(ptr %arg)
ret ptr %p2
}

declare void @no_return()
declare void @foo()
declare ptr @musttail_call(ptr)
66 changes: 33 additions & 33 deletions llvm/test/Transforms/SimplifyCFG/hoist-common-code.ll
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,11 @@ F: ; preds = %0

define void @test_switch(i64 %i, ptr %Q) {
; CHECK-LABEL: @test_switch(
; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: bb0:
; CHECK-NEXT: common.ret:
; CHECK-NEXT: store i32 1, ptr [[Q:%.*]], align 4
; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[Q]], align 4
; CHECK-NEXT: call void @bar(i32 [[A]])
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
; CHECK: bb1:
; CHECK-NEXT: store i32 1, ptr [[Q]], align 4
; CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Q]], align 4
; CHECK-NEXT: call void @bar(i32 [[B]])
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: bb2:
; CHECK-NEXT: store i32 1, ptr [[Q]], align 4
; CHECK-NEXT: [[C:%.*]] = load i32, ptr [[Q]], align 4
; CHECK-NEXT: call void @bar(i32 [[C]])
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK-NEXT: ret void
;
switch i64 %i, label %bb0 [
i64 1, label %bb1
Expand All @@ -69,25 +53,41 @@ bb2: ; preds = %0
ret void
}

define i1 @common_instr_on_switch(i64 %a, i64 %b, i64 %c) unnamed_addr {
; CHECK-LABEL: @common_instr_on_switch(
; CHECK-NEXT: start:
; CHECK-NEXT: switch i64 [[A:%.*]], label [[BB0:%.*]] [
; We ensure that we examine all instructions during each iteration to confirm the presence of a terminating one.
define void @test_switch_reach_terminator(i64 %i, ptr %p) {
; CHECK-LABEL: @test_switch_reach_terminator(
; CHECK-NEXT: switch i64 [[I:%.*]], label [[BB0:%.*]] [
; CHECK-NEXT: i64 1, label [[BB1:%.*]]
; CHECK-NEXT: i64 2, label [[BB2:%.*]]
; CHECK-NEXT: i64 2, label [[COMMON_RET:%.*]]
; CHECK-NEXT: ]
; CHECK: common.ret:
; CHECK-NEXT: ret void
; CHECK: bb0:
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK-NEXT: store i32 1, ptr [[P:%.*]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
; CHECK: bb1:
; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i64 [[B]], [[C]]
; CHECK-NEXT: br label [[EXIT]]
; CHECK: bb2:
; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i64 [[B]], [[C]]
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: [[RESULT:%.*]] = phi i1 [ [[TMP0]], [[BB0]] ], [ [[TMP1]], [[BB1]] ], [ [[TMP2]], [[BB2]] ]
; CHECK-NEXT: ret i1 [[RESULT]]
; CHECK-NEXT: store i32 2, ptr [[P]], align 4
; CHECK-NEXT: br label [[COMMON_RET]]
;
switch i64 %i, label %bb0 [
i64 1, label %bb1
i64 2, label %bb2
]
bb0: ; preds = %0
store i32 1, ptr %p
ret void
bb1: ; preds = %0
store i32 2, ptr %p
ret void
bb2: ; preds = %0
ret void
}

define i1 @common_instr_on_switch(i64 %a, i64 %b, i64 %c) unnamed_addr {
; CHECK-LABEL: @common_instr_on_switch(
; CHECK-NEXT: start:
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[B:%.*]], [[C:%.*]]
; CHECK-NEXT: ret i1 [[TMP0]]
;
start:
switch i64 %a, label %bb0 [
Expand Down
Loading