| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt -S -passes=licm < %s | FileCheck %s |
| |
| ; Hoist ADD and remove old op if unused. |
| define void @add_one_use(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_one_use( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add i64 %index, %c1 |
| %index.next = add i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist MUL and remove old op if unused. |
| define void @mul_one_use(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_one_use( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[FACTOR_OP_MUL:%.*]] = mul i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[STEP_ADD_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD_REASS]] = mul i64 [[INDEX]], [[FACTOR_OP_MUL]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul i64 %index, %c1 |
| %index.next = mul i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist ADD and copy NUW if both ops have it. |
| define void @add_nuw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_nuw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add nuw i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add nuw i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nuw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist ADD and copy NUW if both ops have it. |
| ; Version where operands are commuted. |
| define void @add_nuw_comm(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_nuw_comm( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add nuw i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw i64 [[C1]], [[INDEX]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add nuw i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw i64 %c1, %index |
| call void @use(i64 %step.add) |
| %index.next = add nuw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist ADD and copy NUW if both ops have it. |
| ; Another version where operands are commuted. |
| define void @add_nuw_comm2(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_nuw_comm2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add nuw i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add nuw i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nuw i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Hoist ADD and copy NUW if both ops have it. |
| ; Another version where operands are commuted. |
| define void @add_nuw_comm3(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_nuw_comm3( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add nuw i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw i64 [[C1]], [[INDEX]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add nuw i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw i64 %c1, %index |
| call void @use(i64 %step.add) |
| %index.next = add nuw i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Hoist ADD and copy NUW if both ops have it. |
| ; A version where the LHS and RHS of the outer BinOp are BinOps. |
| define void @add_nuw_twobinops(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_nuw_twobinops( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[C2_PLUS_2:%.*]] = add nuw i64 [[C2:%.*]], 2 |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add nuw i64 [[C1:%.*]], [[C2_PLUS_2]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw i64 [[C1]], [[INDEX]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add nuw i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw i64 %c1, %index |
| call void @use(i64 %step.add) |
| %c2.plus.2 = add nuw i64 %c2, 2 |
| %index.next = add nuw i64 %step.add, %c2.plus.2 |
| br label %loop |
| } |
| |
| ; Hoist MUL and drop NUW even if both ops have it. |
| define void @mul_nuw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_nuw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul nuw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = mul nuw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist MUL and drop NUW even if both ops have it. |
| ; Version where operands are commuted. |
| define void @mul_nuw_comm(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_nuw_comm( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw i64 [[C1]], [[INDEX]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul nuw i64 %c1, %index |
| call void @use(i64 %step.add) |
| %index.next = mul nuw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist MUL and drop NUW even if both ops have it. |
| ; Another version where operands are commuted. |
| define void @mul_nuw_comm2(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_nuw_comm2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul nuw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = mul nuw i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Hoist MUL and drop NUW even if both ops have it. |
| ; Another version where operands are commuted. |
| define void @mul_nuw_comm3(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_nuw_comm3( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw i64 [[C1]], [[INDEX]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul nuw i64 %c1, %index |
| call void @use(i64 %step.add) |
| %index.next = mul nuw i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Hoist MUL and drop NUW even if both ops have it. |
| ; A version where the LHS and RHS of the outer BinOp are BinOps. |
| define void @mul_nuw_twobinops(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_nuw_twobinops( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[C2_PLUS_2:%.*]] = add nuw i64 [[C2:%.*]], 2 |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul i64 [[C1:%.*]], [[C2_PLUS_2]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw i64 [[C1]], [[INDEX]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul nuw i64 %c1, %index |
| call void @use(i64 %step.add) |
| %c2.plus.2 = add nuw i64 %c2, 2 |
| %index.next = mul nuw i64 %step.add, %c2.plus.2 |
| br label %loop |
| } |
| |
| ; Hoist ADD but don't copy NUW if only one op has it. |
| define void @add_no_nuw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_no_nuw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nuw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist ADD but don't copy NSW if one op has it. |
| define void @add_no_nsw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_no_nsw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nsw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Hoist ADD but don't copy NSW even if both ops have it. |
| define void @add_no_nsw_2(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_no_nsw_2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nsw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nsw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nsw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; FIXME: Hoist ADD and copy NUW NSW if both ops have it. |
| define void @add_nuw_nsw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_nuw_nsw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add nuw nsw i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw nsw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add nuw nsw i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw nsw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nuw nsw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| define void @add_both_nsw_first_nuw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_both_nsw_first_nuw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nuw nsw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nuw nsw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nsw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| define void @add_both_nsw_second_nuw(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @add_both_nsw_second_nuw( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add nsw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = add i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add nsw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add nuw nsw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; |
| ; Hoist MUL and drop NSW even if both ops have it. |
| define void @mul_no_nsw_2(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @mul_no_nsw_2( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nsw i64 [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = mul nsw i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = mul nsw i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Don't hoist if the ops are different (even if they are both associative). |
| define void @diff_ops(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @diff_ops( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add i64 [[INDEX]], [[C1:%.*]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT]] = mul i64 [[STEP_ADD]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = mul i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Don't hoist if the ops are not associative. |
| define void @noassoc_ops(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @noassoc_ops( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = sub i64 [[INDEX]], [[C1:%.*]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT]] = sub i64 [[STEP_ADD]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = sub i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = sub i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; The simple case. Hoist if fast is present on both instructions. |
| define void @fadd_fast(float %c1, float %c2) { |
| ; CHECK-LABEL: @fadd_fast( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fadd fast float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fadd fast float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fadd fast float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fadd fast float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fadd fast float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; The simple case. Hoist if fast is present on both instructions. |
| define void @fmul_fast(float %c1, float %c2) { |
| ; CHECK-LABEL: @fmul_fast( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fmul fast float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fmul fast float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fmul fast float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fmul fast float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fmul fast float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; The minimum case. |
| ; Hoist if reasassoc and nsz are present on both instructions. |
| define void @fadd_reassoc_nsz(float %c1, float %c2) { |
| ; CHECK-LABEL: @fadd_reassoc_nsz( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fadd reassoc nsz float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fadd reassoc nsz float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fadd reassoc nsz float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fadd reassoc nsz float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fadd reassoc nsz float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; The minimum case. |
| ; Hoist if reasassoc and nsz are present on both instructions. |
| define void @fmul_reassoc_nsz(float %c1, float %c2) { |
| ; CHECK-LABEL: @fmul_reassoc_nsz( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fmul reassoc nsz float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fmul reassoc nsz float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fmul reassoc nsz float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fmul reassoc nsz float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fmul reassoc nsz float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Don't hoist if both reassoc and nsz aren't present on both instructions. |
| define void @fadd_nonassoc(float %c1, float %c2) { |
| ; CHECK-LABEL: @fadd_nonassoc( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fadd reassoc float [[INDEX]], [[C1:%.*]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT]] = fadd reassoc nsz float [[STEP_ADD]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fadd reassoc float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fadd reassoc nsz float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Don't hoist if both reassoc and nsz aren't present on both instructions. |
| define void @fmul_noassoc(float %c1, float %c2) { |
| ; CHECK-LABEL: @fmul_noassoc( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fmul reassoc nsz float [[INDEX]], [[C1:%.*]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT]] = fmul nsz float [[STEP_ADD]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fmul reassoc nsz float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fmul nsz float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; No intersection in flags present on both instructions, |
| ; except reassoc and nsz. |
| define void @fadd_fmf_nointersect(float %c1, float %c2) { |
| ; CHECK-LABEL: @fadd_fmf_nointersect( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fadd reassoc nsz float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fadd reassoc nnan nsz float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fadd reassoc nsz float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fadd reassoc nsz nnan float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fadd reassoc nsz ninf float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; No intersection in flags present on both instructions, |
| ; except reassoc and nsz. |
| define void @fmul_fmf_nointersect(float %c1, float %c2) { |
| ; CHECK-LABEL: @fmul_fmf_nointersect( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fmul reassoc nsz float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fmul reassoc nsz contract float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fmul reassoc nsz float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fmul reassoc nsz contract float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fmul reassoc nnan nsz float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Non-empty intersection in flags present on both instructions, |
| ; including reassoc and nsz. |
| define void @fadd_fmf_intersect(float %c1, float %c2) { |
| ; CHECK-LABEL: @fadd_fmf_intersect( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fadd reassoc ninf nsz float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fadd reassoc nnan ninf nsz float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fadd reassoc ninf nsz float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fadd reassoc nnan nsz ninf float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fadd reassoc ninf nsz float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Non-empty intersection in flags present on both instructions, |
| ; including reassoc and nsz. |
| define void @fmul_fmf_intersect(float %c1, float %c2) { |
| ; CHECK-LABEL: @fmul_fmf_intersect( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = fmul reassoc nsz afn float [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi float [ 0.000000e+00, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = fmul reassoc nsz arcp afn float [[INDEX]], [[C1]] |
| ; CHECK-NEXT: call void @use(float [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = fmul reassoc nsz afn float [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi float [ 0., %entry ], [ %index.next, %loop ] |
| %step.add = fmul reassoc afn nsz arcp float %index, %c1 |
| call void @use(float %step.add) |
| %index.next = fmul reassoc nsz afn float %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Trivially hoist and. |
| define void @and(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @and( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = and i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = and i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = and i64 %index, %c1 |
| %index.next = and i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Trivially hoist or. |
| define void @or(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @or( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = or i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = or i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = or i64 %index, %c1 |
| %index.next = or i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Trivially hoist or disjoint. |
| define void @or_all_disjoint(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @or_all_disjoint( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = or disjoint i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = or disjoint i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = or disjoint i64 %index, %c1 |
| %index.next = or disjoint i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Trivially hoist or, disjoint on first or only . |
| define void @or_disjoint_on_first_or_only(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @or_disjoint_on_first_or_only( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = or i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = or i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = or i64 %index, %c1 |
| %index.next = or disjoint i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Trivially hoist or, disjoint on second or only . |
| define void @or_disjoint_on_second_or_only(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @or_disjoint_on_second_or_only( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = or i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = or i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = or disjoint i64 %index, %c1 |
| %index.next = or i64 %c2, %step.add |
| br label %loop |
| } |
| |
| ; Trivially hoist xor. |
| define void @xor(i64 %c1, i64 %c2) { |
| ; CHECK-LABEL: @xor( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = xor i64 [[C1:%.*]], [[C2:%.*]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = xor i64 [[INDEX]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = xor i64 %c1, %index |
| %index.next = xor i64 %step.add, %c2 |
| br label %loop |
| } |
| |
| ; Don't hoist if the intermediate op has more than two uses. This is an |
| ; heuristic that can be adjusted if warranted. Currently we are being |
| ; conservative to minimise potential impact in code size. |
| define void @not_many_uses(i64 %c1, i64 %c2, i64 %c3) { |
| ; CHECK-LABEL: @not_many_uses( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add i64 [[INDEX]], [[C1:%.*]] |
| ; CHECK-NEXT: call void @use(i64 [[STEP_ADD]]) |
| ; CHECK-NEXT: [[INDEX_NEXT]] = add i64 [[STEP_ADD]], [[C2:%.*]] |
| ; CHECK-NEXT: [[OTHER:%.*]] = add i64 [[STEP_ADD]], [[C3:%.*]] |
| ; CHECK-NEXT: call void @use(i64 [[OTHER]]) |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| br label %loop |
| |
| loop: |
| %index = phi i64 [ 0, %entry ], [ %index.next, %loop ] |
| %step.add = add i64 %index, %c1 |
| call void @use(i64 %step.add) |
| %index.next = add i64 %step.add, %c2 |
| %other = add i64 %step.add, %c3 |
| call void @use(i64 %other) |
| br label %loop |
| } |
| |
| ; Original reproducer, adapted from: |
| ; for(long i = 0; i < n; ++i) |
| ; a[i] = (i*k) * v; |
| define void @test(i64 %n, i64 %k) { |
| ; CHECK-LABEL: @test( |
| ; CHECK-NEXT: entry: |
| ; CHECK-NEXT: [[K_2:%.*]] = shl nuw nsw i64 [[K:%.*]], 1 |
| ; CHECK-NEXT: [[VEC_INIT:%.*]] = insertelement <2 x i64> zeroinitializer, i64 [[K]], i64 1 |
| ; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <2 x i64> poison, i64 [[K_2]], i64 0 |
| ; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <2 x i64> [[DOTSPLATINSERT]], <2 x i64> poison, <2 x i32> zeroinitializer |
| ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = add <2 x i64> [[DOTSPLAT]], [[DOTSPLAT]] |
| ; CHECK-NEXT: br label [[LOOP:%.*]] |
| ; CHECK: loop: |
| ; CHECK-NEXT: [[VEC_IND:%.*]] = phi <2 x i64> [ [[VEC_INIT]], [[ENTRY:%.*]] ], [ [[VEC_IND_NEXT_REASS:%.*]], [[LOOP]] ] |
| ; CHECK-NEXT: [[STEP_ADD:%.*]] = add <2 x i64> [[VEC_IND]], [[DOTSPLAT]] |
| ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) |
| ; CHECK-NEXT: [[VEC_IND_NEXT_REASS]] = add <2 x i64> [[VEC_IND]], [[INVARIANT_OP]] |
| ; CHECK-NEXT: br label [[LOOP]] |
| ; |
| entry: |
| %k.2 = shl nuw nsw i64 %k, 1 |
| %vec.init = insertelement <2 x i64> zeroinitializer, i64 %k, i64 1 |
| %.splatinsert = insertelement <2 x i64> poison, i64 %k.2, i64 0 |
| %.splat = shufflevector <2 x i64> %.splatinsert, <2 x i64> poison, <2 x i32> zeroinitializer |
| br label %loop |
| |
| loop: |
| %vec.ind = phi <2 x i64> [ %vec.init, %entry ], [ %vec.ind.next, %loop ] |
| %step.add = add <2 x i64> %vec.ind, %.splat |
| call void @use(<2 x i64> %step.add) |
| %vec.ind.next = add <2 x i64> %step.add, %.splat |
| br label %loop |
| } |
| |
| declare void @use() |