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