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