Yingwei Zheng 76c69ca29c
[ValueTracking] Bail out on non-immediate constant expressions (#168084)
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
2025-11-15 21:14:29 +08:00

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) }