blob: f51e77ae827275b08564b5efacdc55c796497c8a [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt < %s -passes=sccp -S | FileCheck %s
declare i1 @cond()
declare { i32, i32 } @get_pair()
declare void @use(i32)
; The condition is a known constant, so the true operand is selected and both
; elements are known.
define { i32, i32 } @select_true() {
; CHECK-LABEL: define { i32, i32 } @select_true() {
; CHECK-NEXT: ret { i32, i32 } { i32 1, i32 2 }
;
%s = select i1 true, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
ret { i32, i32 } %s
}
; Same, but the false operand is selected.
define { i32, i32 } @select_false() {
; CHECK-LABEL: define { i32, i32 } @select_false() {
; CHECK-NEXT: ret { i32, i32 } { i32 3, i32 4 }
;
%s = select i1 false, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
ret { i32, i32 } %s
}
; The condition is only known to be constant after solving.
define { i32, i32 } @select_resolved_cond(i32 %x) {
; CHECK-LABEL: define { i32, i32 } @select_resolved_cond(
; CHECK-SAME: i32 [[X:%.*]]) {
; CHECK-NEXT: ret { i32, i32 } { i32 1, i32 2 }
;
%and = and i32 %x, 0
%c = icmp eq i32 %and, 0
%s = select i1 %c, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
ret { i32, i32 } %s
}
; The selected operand is built by insertvalue, which is tracked per element.
define { i32, i32 } @select_insertvalue() {
; CHECK-LABEL: define { i32, i32 } @select_insertvalue() {
; CHECK-NEXT: ret { i32, i32 } { i32 7, i32 8 }
;
%i0 = insertvalue { i32, i32 } poison, i32 7, 0
%i1 = insertvalue { i32, i32 } %i0, i32 8, 1
%s = select i1 true, { i32, i32 } %i1, { i32, i32 } zeroinitializer
ret { i32, i32 } %s
}
; Only element 0 of the selected operand is known; element 1 is overdefined.
; The select itself cannot be folded, but the extractvalue of element 0 can.
define i32 @select_partially_known() {
; CHECK-LABEL: define i32 @select_partially_known() {
; CHECK-NEXT: [[CALL:%.*]] = call { i32, i32 } @get_pair()
; CHECK-NEXT: [[INS:%.*]] = insertvalue { i32, i32 } [[CALL]], i32 42, 0
; CHECK-NEXT: [[S:%.*]] = select i1 true, { i32, i32 } [[INS]], { i32, i32 } zeroinitializer
; CHECK-NEXT: [[E1:%.*]] = extractvalue { i32, i32 } [[S]], 1
; CHECK-NEXT: call void @use(i32 [[E1]])
; CHECK-NEXT: ret i32 42
;
%call = call { i32, i32 } @get_pair()
%ins = insertvalue { i32, i32 } %call, i32 42, 0
%s = select i1 true, { i32, i32 } %ins, { i32, i32 } zeroinitializer
%e0 = extractvalue { i32, i32 } %s, 0
%e1 = extractvalue { i32, i32 } %s, 1
call void @use(i32 %e1)
ret i32 %e0
}
; A non-constant condition merges both operands per field. Here they disagree in
; both fields, so nothing is folded.
define { i32, i32 } @select_unknown_cond() {
; CHECK-LABEL: define { i32, i32 } @select_unknown_cond() {
; CHECK-NEXT: [[C:%.*]] = call i1 @cond()
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
; CHECK-NEXT: ret { i32, i32 } [[S]]
;
%c = call i1 @cond()
%s = select i1 %c, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
ret { i32, i32 } %s
}
; Both operands agree in every field, so the result is known even though the
; condition is not.
define { i32, i32 } @select_unknown_cond_same_operands() {
; CHECK-LABEL: define { i32, i32 } @select_unknown_cond_same_operands() {
; CHECK-NEXT: [[C:%.*]] = call i1 @cond()
; CHECK-NEXT: ret { i32, i32 } { i32 1, i32 2 }
;
%c = call i1 @cond()
%s = select i1 %c, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 1, i32 2 }
ret { i32, i32 } %s
}
; The operands agree in field 0 only, so the extractvalue of field 0 folds while
; field 1 stays overdefined.
define i32 @select_unknown_cond_one_field_agrees() {
; CHECK-LABEL: define i32 @select_unknown_cond_one_field_agrees() {
; CHECK-NEXT: [[C:%.*]] = call i1 @cond()
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], { i32, i32 } { i32 9, i32 2 }, { i32, i32 } { i32 9, i32 4 }
; CHECK-NEXT: [[E1:%.*]] = extractvalue { i32, i32 } [[S]], 1
; CHECK-NEXT: call void @use(i32 [[E1]])
; CHECK-NEXT: ret i32 9
;
%c = call i1 @cond()
%s = select i1 %c, { i32, i32 } { i32 9, i32 2 }, { i32, i32 } { i32 9, i32 4 }
%e0 = extractvalue { i32, i32 } %s, 0
%e1 = extractvalue { i32, i32 } %s, 1
call void @use(i32 %e1)
ret i32 %e0
}
; The merge of the two fields is a constant range, not a single constant, so the
; select is not folded but the range is available to later users.
define i32 @select_unknown_cond_range() {
; CHECK-LABEL: define i32 @select_unknown_cond_range() {
; CHECK-NEXT: [[C:%.*]] = call i1 @cond()
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], { i32, i32 } { i32 1, i32 0 }, { i32, i32 } { i32 3, i32 0 }
; CHECK-NEXT: [[E0:%.*]] = extractvalue { i32, i32 } [[S]], 0
; CHECK-NEXT: ret i32 1
;
%c = call i1 @cond()
%s = select i1 %c, { i32, i32 } { i32 1, i32 0 }, { i32, i32 } { i32 3, i32 0 }
%e0 = extractvalue { i32, i32 } %s, 0
%cmp = icmp ult i32 %e0, 10
%z = zext i1 %cmp to i32
ret i32 %z
}
; An unknown condition with one operand coming from an untracked call leaves the
; corresponding field overdefined, but the other field still merges.
define { i32, i32 } @select_unknown_cond_partial() {
; CHECK-LABEL: define { i32, i32 } @select_unknown_cond_partial() {
; CHECK-NEXT: [[CALL:%.*]] = call { i32, i32 } @get_pair()
; CHECK-NEXT: [[INS:%.*]] = insertvalue { i32, i32 } [[CALL]], i32 5, 0
; CHECK-NEXT: [[C:%.*]] = call i1 @cond()
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], { i32, i32 } [[INS]], { i32, i32 } { i32 5, i32 7 }
; CHECK-NEXT: ret { i32, i32 } [[S]]
;
%call = call { i32, i32 } @get_pair()
%ins = insertvalue { i32, i32 } %call, i32 5, 0
%c = call i1 @cond()
%s = select i1 %c, { i32, i32 } %ins, { i32, i32 } { i32 5, i32 7 }
ret { i32, i32 } %s
}
; Nested struct fields are compared as whole values, so agreeing operands fold.
define { { i32, i32 }, i32 } @select_unknown_cond_nested() {
; CHECK-LABEL: define { { i32, i32 }, i32 } @select_unknown_cond_nested() {
; CHECK-NEXT: [[C:%.*]] = call i1 @cond()
; CHECK-NEXT: ret { { i32, i32 }, i32 } { { i32, i32 } { i32 1, i32 2 }, i32 3 }
;
%c = call i1 @cond()
%s = select i1 %c, { { i32, i32 }, i32 } { { i32, i32 } { i32 1, i32 2 }, i32 3 }, { { i32, i32 }, i32 } { { i32, i32 } { i32 1, i32 2 }, i32 3 }
ret { { i32, i32 }, i32 } %s
}
define { i32, i32 } @select_poison_cond() {
; CHECK-LABEL: define { i32, i32 } @select_poison_cond() {
; CHECK-NEXT: [[S:%.*]] = select i1 poison, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
; CHECK-NEXT: ret { i32, i32 } [[S]]
;
%s = select i1 poison, { i32, i32 } { i32 1, i32 2 }, { i32, i32 } { i32 3, i32 4 }
ret { i32, i32 } %s
}
; Nested structs are propagated as a single element value.
define { { i32, i32 }, i32 } @select_nested_struct() {
; CHECK-LABEL: define { { i32, i32 }, i32 } @select_nested_struct() {
; CHECK-NEXT: ret { { i32, i32 }, i32 } { { i32, i32 } { i32 1, i32 2 }, i32 3 }
;
%s = select i1 true, { { i32, i32 }, i32 } { { i32, i32 } { i32 1, i32 2 }, i32 3 }, { { i32, i32 }, i32 } zeroinitializer
ret { { i32, i32 }, i32 } %s
}
; Many elements, to exercise growth of the internal per-element lattice map.
define { i32, i32, i32, i32, i32, i32, i32, i32 } @select_many_elements() {
; CHECK-LABEL: define { i32, i32, i32, i32, i32, i32, i32, i32 } @select_many_elements() {
; CHECK-NEXT: ret { i32, i32, i32, i32, i32, i32, i32, i32 } { i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7 }
;
%s = select i1 true, { i32, i32, i32, i32, i32, i32, i32, i32 } { i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7 }, { i32, i32, i32, i32, i32, i32, i32, i32 } zeroinitializer
ret { i32, i32, i32, i32, i32, i32, i32, i32 } %s
}
; The select feeds a phi that feeds it back, which must still converge.
define { i32, i32 } @select_in_loop(i1 %exit) {
; CHECK-LABEL: define { i32, i32 } @select_in_loop(
; CHECK-SAME: i1 [[EXIT:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: br label %[[LOOP:.*]]
; CHECK: [[LOOP]]:
; CHECK-NEXT: br i1 [[EXIT]], label %[[OUT:.*]], label %[[LOOP]]
; CHECK: [[OUT]]:
; CHECK-NEXT: ret { i32, i32 } { i32 1, i32 2 }
;
entry:
br label %loop
loop:
%p = phi { i32, i32 } [ { i32 1, i32 2 }, %entry ], [ %s, %loop ]
%s = select i1 true, { i32, i32 } %p, { i32, i32 } { i32 3, i32 4 }
br i1 %exit, label %out, label %loop
out:
ret { i32, i32 } %s
}