In https://github.com/llvm/llvm-project/pull/165748 constant expressions were allowed in `collectPossibleValues` because we are still using insertelement + shufflevector idioms to represent a scalable vector splat. However, it also accepts some unresolved constants like ptrtoint of globals or pointer difference between two globals. Absolutely we can ask the user to check this case with the constant folding API. However, since we don't observe the real-world usefulness of handling constant expressions, I decide to be more conservative and only handle immediate constants in the helper function. With this patch, we don't need to touch the SimplifyCFG part, as the values can only be either ConstantInt or undef/poison values (NB: switch on undef condition is UB). Fix the miscompilation reported by https://github.com/llvm/llvm-project/pull/165748#issuecomment-3532245218
318 lines
8.5 KiB
LLVM
318 lines
8.5 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S | FileCheck %s
|
|
|
|
; Test basic folding to a conditional branch.
|
|
define i32 @foo(i64 %x, i64 %y) nounwind {
|
|
; CHECK-LABEL: @foo(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[EQ:%.*]] = icmp eq i64 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: br i1 [[EQ]], label [[B:%.*]], label [[SWITCH:%.*]]
|
|
; CHECK: switch:
|
|
; CHECK-NEXT: [[LT:%.*]] = icmp slt i64 [[X]], [[Y]]
|
|
; CHECK-NEXT: br i1 [[LT]], label [[A:%.*]], label [[B]]
|
|
; CHECK: common.ret:
|
|
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = phi i32 [ 1, [[A]] ], [ [[RETVAL:%.*]], [[B]] ]
|
|
; CHECK-NEXT: ret i32 [[COMMON_RET_OP]]
|
|
; CHECK: a:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0:[0-9]+]]
|
|
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
|
; CHECK: b:
|
|
; CHECK-NEXT: [[RETVAL]] = phi i32 [ 0, [[SWITCH]] ], [ 2, [[ENTRY:%.*]] ]
|
|
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[COMMON_RET]]
|
|
;
|
|
entry:
|
|
%eq = icmp eq i64 %x, %y
|
|
br i1 %eq, label %b, label %switch
|
|
switch:
|
|
%lt = icmp slt i64 %x, %y
|
|
%qux = select i1 %lt, i32 0, i32 2
|
|
switch i32 %qux, label %bees [
|
|
i32 0, label %a
|
|
i32 1, label %b
|
|
i32 2, label %b
|
|
]
|
|
a:
|
|
tail call void @bees.a() nounwind
|
|
ret i32 1
|
|
b:
|
|
%retval = phi i32 [0, %switch], [0, %switch], [2, %entry]
|
|
tail call void @bees.b() nounwind
|
|
ret i32 %retval
|
|
bees:
|
|
tail call void @llvm.trap() nounwind
|
|
unreachable
|
|
}
|
|
|
|
; Test basic folding to an unconditional branch.
|
|
define i32 @bar(i64 %x, i64 %y) nounwind {
|
|
; CHECK-LABEL: @bar(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
entry:
|
|
%lt = icmp slt i64 %x, %y
|
|
%qux = select i1 %lt, i32 0, i32 2
|
|
switch i32 %qux, label %bees [
|
|
i32 0, label %a
|
|
i32 1, label %b
|
|
i32 2, label %a
|
|
]
|
|
a:
|
|
%retval = phi i32 [0, %entry], [0, %entry], [1, %b]
|
|
tail call void @bees.a() nounwind
|
|
ret i32 0
|
|
b:
|
|
tail call void @bees.b() nounwind
|
|
br label %a
|
|
bees:
|
|
tail call void @llvm.trap() nounwind
|
|
unreachable
|
|
}
|
|
|
|
; Test the edge case where both values from the select are the default case.
|
|
define void @bazz(i64 %x, i64 %y) nounwind {
|
|
; CHECK-LABEL: @bazz(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%lt = icmp slt i64 %x, %y
|
|
%qux = select i1 %lt, i32 10, i32 12
|
|
switch i32 %qux, label %b [
|
|
i32 0, label %a
|
|
i32 1, label %bees
|
|
i32 2, label %bees
|
|
]
|
|
a:
|
|
tail call void @bees.a() nounwind
|
|
ret void
|
|
b:
|
|
tail call void @bees.b() nounwind
|
|
ret void
|
|
bees:
|
|
tail call void @llvm.trap()
|
|
unreachable
|
|
}
|
|
|
|
; Test the edge case where both values from the select are equal.
|
|
define void @quux(i64 %x, i64 %y) nounwind {
|
|
; CHECK-LABEL: @quux(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
%lt = icmp slt i64 %x, %y
|
|
%qux = select i1 %lt, i32 0, i32 0
|
|
switch i32 %qux, label %b [
|
|
i32 0, label %a
|
|
i32 1, label %bees
|
|
i32 2, label %bees
|
|
]
|
|
a:
|
|
tail call void @bees.a() nounwind
|
|
ret void
|
|
b:
|
|
tail call void @bees.b() nounwind
|
|
ret void
|
|
bees:
|
|
tail call void @llvm.trap()
|
|
unreachable
|
|
}
|
|
|
|
; A final test, for phi node munging.
|
|
define i32 @xyzzy(i64 %x, i64 %y) {
|
|
; CHECK-LABEL: @xyzzy(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[EQ:%.*]] = icmp eq i64 [[X:%.*]], [[Y:%.*]]
|
|
; CHECK-NEXT: [[LT:%.*]] = icmp slt i64 [[X]], [[Y]]
|
|
; CHECK-NEXT: [[SPEC_SELECT:%.*]] = select i1 [[LT]], i32 -1, i32 1
|
|
; CHECK-NEXT: [[COMMON_RET_OP:%.*]] = select i1 [[EQ]], i32 0, i32 [[SPEC_SELECT]]
|
|
; CHECK-NEXT: ret i32 [[COMMON_RET_OP]]
|
|
;
|
|
entry:
|
|
%eq = icmp eq i64 %x, %y
|
|
br i1 %eq, label %r, label %cont
|
|
cont:
|
|
%lt = icmp slt i64 %x, %y
|
|
%qux = select i1 %lt, i32 0, i32 2
|
|
switch i32 %qux, label %bees [
|
|
i32 0, label %a
|
|
i32 1, label %r
|
|
i32 2, label %r
|
|
]
|
|
r:
|
|
%val = phi i32 [0, %entry], [1, %cont], [1, %cont]
|
|
ret i32 %val
|
|
a:
|
|
ret i32 -1
|
|
bees:
|
|
tail call void @llvm.trap()
|
|
unreachable
|
|
}
|
|
|
|
define void @pr165179(i1 %cond) {
|
|
; CHECK-LABEL: @pr165179(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[SWITCHBB:%.*]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[SWITCHBB]]
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
entry:
|
|
br i1 %cond, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
tail call void @bees.a() nounwind
|
|
br label %switchbb
|
|
|
|
if.else:
|
|
tail call void @bees.b() nounwind
|
|
br label %switchbb
|
|
|
|
switchbb:
|
|
%cond1 = phi i32 [ 1, %if.else ], [ -1, %if.then ]
|
|
switch i32 %cond1, label %default [
|
|
i32 1, label %exit
|
|
i32 -1, label %exit
|
|
]
|
|
|
|
exit:
|
|
tail call void @bees.a() nounwind
|
|
ret void
|
|
|
|
default:
|
|
tail call void @bees.b() nounwind
|
|
ret void
|
|
}
|
|
|
|
define void @switch_remove_dead_case_phi(i1 %cond1, i1 %cond2) {
|
|
; CHECK-LABEL: @switch_remove_dead_case_phi(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: br i1 [[COND1:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
|
; CHECK: if.then:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: br i1 [[COND2:%.*]], label [[SWITCHBB:%.*]], label [[IF_ELSE]]
|
|
; CHECK: if.else:
|
|
; CHECK-NEXT: [[PHI:%.*]] = phi i32 [ 3, [[ENTRY:%.*]] ], [ -1, [[IF_THEN]] ]
|
|
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[SWITCHBB]]
|
|
; CHECK: switchbb:
|
|
; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[PHI]], [[IF_ELSE]] ], [ 5, [[IF_THEN]] ]
|
|
; CHECK-NEXT: [[COND3:%.*]] = icmp eq i32 [[COND]], -1
|
|
; CHECK-NEXT: br i1 [[COND3]], label [[EXIT:%.*]], label [[DEFAULT:%.*]]
|
|
; CHECK: common.ret:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
|
; CHECK: default:
|
|
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[COMMON_RET]]
|
|
;
|
|
entry:
|
|
br i1 %cond1, label %if.then, label %if.else
|
|
|
|
if.then:
|
|
tail call void @bees.a() nounwind
|
|
br i1 %cond2, label %switchbb, label %if.else
|
|
|
|
if.else:
|
|
%phi = phi i32 [ 3, %entry ], [ -1, %if.then ]
|
|
tail call void @bees.b() nounwind
|
|
br label %switchbb
|
|
|
|
switchbb:
|
|
%cond = phi i32 [ %phi, %if.else ], [ 5, %if.then ]
|
|
switch i32 %cond, label %default [
|
|
i32 1, label %exit
|
|
i32 -1, label %exit
|
|
]
|
|
|
|
exit:
|
|
tail call void @bees.a() nounwind
|
|
ret void
|
|
|
|
default:
|
|
tail call void @bees.b() nounwind
|
|
ret void
|
|
}
|
|
|
|
define void @switch_remove_dead_case_select(i1 %cond1, i1 %cond2) {
|
|
; CHECK-LABEL: @switch_remove_dead_case_select(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[X:%.*]] = select i1 [[COND1:%.*]], i32 -1, i32 3
|
|
; CHECK-NEXT: [[Y:%.*]] = select i1 [[COND2:%.*]], i32 [[X]], i32 5
|
|
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[Y]], -1
|
|
; CHECK-NEXT: br i1 [[COND]], label [[EXIT:%.*]], label [[DEFAULT:%.*]]
|
|
; CHECK: common.ret:
|
|
; CHECK-NEXT: ret void
|
|
; CHECK: exit:
|
|
; CHECK-NEXT: tail call void @bees.a() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[COMMON_RET:%.*]]
|
|
; CHECK: default:
|
|
; CHECK-NEXT: tail call void @bees.b() #[[ATTR0]]
|
|
; CHECK-NEXT: br label [[COMMON_RET]]
|
|
;
|
|
entry:
|
|
%x = select i1 %cond1, i32 -1, i32 3
|
|
%y = select i1 %cond2, i32 %x, i32 5
|
|
switch i32 %y, label %default [
|
|
i32 1, label %exit
|
|
i32 -1, label %exit
|
|
]
|
|
|
|
exit:
|
|
tail call void @bees.a() nounwind
|
|
ret void
|
|
|
|
default:
|
|
tail call void @bees.b() nounwind
|
|
ret void
|
|
}
|
|
|
|
@G = constant i16 zeroinitializer, align 1
|
|
|
|
; Make sure we don't remove edges when the condition is a unresolved constant expression.
|
|
|
|
define i16 @switch_on_nonimmediate_constant_expr() {
|
|
; CHECK-LABEL: @switch_on_nonimmediate_constant_expr(
|
|
; CHECK-NEXT: entry:
|
|
; CHECK-NEXT: [[SWITCH_SELECTCMP:%.*]] = icmp eq i32 ptrtoint (ptr @G to i32), 2
|
|
; CHECK-NEXT: [[SWITCH_SELECT:%.*]] = select i1 [[SWITCH_SELECTCMP]], i16 456, i16 789
|
|
; CHECK-NEXT: [[SWITCH_SELECTCMP1:%.*]] = icmp eq i32 ptrtoint (ptr @G to i32), 1
|
|
; CHECK-NEXT: [[SWITCH_SELECT2:%.*]] = select i1 [[SWITCH_SELECTCMP1]], i16 123, i16 [[SWITCH_SELECT]]
|
|
; CHECK-NEXT: ret i16 [[SWITCH_SELECT2]]
|
|
;
|
|
entry:
|
|
switch i32 ptrtoint (ptr @G to i32), label %sw.default [
|
|
i32 1, label %sw.bb
|
|
i32 2, label %sw.bb1
|
|
]
|
|
|
|
sw.bb:
|
|
ret i16 123
|
|
|
|
sw.bb1:
|
|
ret i16 456
|
|
|
|
sw.default:
|
|
ret i16 789
|
|
}
|
|
|
|
|
|
declare void @llvm.trap() nounwind noreturn
|
|
declare void @bees.a() nounwind
|
|
declare void @bees.b() nounwind
|
|
|
|
; CHECK: attributes #1 = { cold noreturn nounwind memory(inaccessiblemem: write) }
|