; 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 } ; Don't hoist ADD if the op has more than one use. define void @add_two_uses(i64 %c1, i64 %c2) { ; CHECK-LABEL: @add_two_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: call void @use(i64 [[INDEX_NEXT]]) ; 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 call void @use(i64 %index.next) 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: [[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 %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: [[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 %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: [[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 %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: [[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 %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: [[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 %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(<2 x i64> %c1, <2 x i64> %c2) { ; CHECK-LABEL: @mul_nuw( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul <2 x i64> [[C1:%.*]], [[C2:%.*]] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[INDEX:%.*]] = phi <2 x i64> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw <2 x i64> [[INDEX]], [[C1]] ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul <2 x i64> [[INDEX]], [[INVARIANT_OP]] ; CHECK-NEXT: br label [[LOOP]] ; entry: br label %loop loop: %index = phi <2 x i64> [ zeroinitializer, %entry ], [ %index.next, %loop ] %step.add = mul nuw <2 x i64> %index, %c1 call void @use(<2 x i64> %step.add) %index.next = mul nuw <2 x 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(<2 x i64> %c1, <2 x i64> %c2) { ; CHECK-LABEL: @mul_nuw_comm( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul <2 x i64> [[C1:%.*]], [[C2:%.*]] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[INDEX:%.*]] = phi <2 x i64> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw <2 x i64> [[C1]], [[INDEX]] ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul <2 x i64> [[INDEX]], [[INVARIANT_OP]] ; CHECK-NEXT: br label [[LOOP]] ; entry: br label %loop loop: %index = phi <2 x i64> [ zeroinitializer, %entry ], [ %index.next, %loop ] %step.add = mul nuw <2 x i64> %c1, %index call void @use(<2 x i64> %step.add) %index.next = mul nuw <2 x 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(<2 x i64> %c1, <2 x i64> %c2) { ; CHECK-LABEL: @mul_nuw_comm2( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul <2 x i64> [[C1:%.*]], [[C2:%.*]] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[INDEX:%.*]] = phi <2 x i64> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw <2 x i64> [[INDEX]], [[C1]] ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul <2 x i64> [[INDEX]], [[INVARIANT_OP]] ; CHECK-NEXT: br label [[LOOP]] ; entry: br label %loop loop: %index = phi <2 x i64> [ zeroinitializer, %entry ], [ %index.next, %loop ] %step.add = mul nuw <2 x i64> %index, %c1 call void @use(<2 x i64> %step.add) %index.next = mul nuw <2 x 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(<2 x i64> %c1, <2 x i64> %c2) { ; CHECK-LABEL: @mul_nuw_comm3( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul <2 x i64> [[C1:%.*]], [[C2:%.*]] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[INDEX:%.*]] = phi <2 x i64> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw <2 x i64> [[C1]], [[INDEX]] ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul <2 x i64> [[INDEX]], [[INVARIANT_OP]] ; CHECK-NEXT: br label [[LOOP]] ; entry: br label %loop loop: %index = phi <2 x i64> [ zeroinitializer, %entry ], [ %index.next, %loop ] %step.add = mul nuw <2 x i64> %c1, %index call void @use(<2 x i64> %step.add) %index.next = mul nuw <2 x 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(<2 x i64> %c1, <2 x i64> %c2) { ; CHECK-LABEL: @mul_nuw_twobinops( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[C2_PLUS_2:%.*]] = add nuw <2 x i64> [[C2:%.*]], splat (i64 2) ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul <2 x i64> [[C1:%.*]], [[C2_PLUS_2]] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[INDEX:%.*]] = phi <2 x i64> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nuw <2 x i64> [[C1]], [[INDEX]] ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul <2 x i64> [[INDEX]], [[INVARIANT_OP]] ; CHECK-NEXT: br label [[LOOP]] ; entry: br label %loop loop: %index = phi <2 x i64> [ zeroinitializer, %entry ], [ %index.next, %loop ] %step.add = mul nuw <2 x i64> %c1, %index call void @use(<2 x i64> %step.add) %c2.plus.2 = add nuw <2 x i64> %c2, %index.next = mul nuw <2 x 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: [[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 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: [[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 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: [[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 %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: [[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 %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: [[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 %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: [[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 %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(<2 x i64> %c1, <2 x i64> %c2) { ; CHECK-LABEL: @mul_no_nsw_2( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[INVARIANT_OP:%.*]] = mul <2 x i64> [[C1:%.*]], [[C2:%.*]] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[INDEX:%.*]] = phi <2 x i64> [ zeroinitializer, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ] ; CHECK-NEXT: [[STEP_ADD:%.*]] = mul nsw <2 x i64> [[INDEX]], [[C1]] ; CHECK-NEXT: call void @use(<2 x i64> [[STEP_ADD]]) ; CHECK-NEXT: [[INDEX_NEXT_REASS]] = mul <2 x i64> [[INDEX]], [[INVARIANT_OP]] ; CHECK-NEXT: br label [[LOOP]] ; entry: br label %loop loop: %index = phi <2 x i64> [ zeroinitializer, %entry ], [ %index.next, %loop ] %step.add = mul nsw <2 x i64> %index, %c1 call void @use(<2 x i64> %step.add) %index.next = mul nsw <2 x 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: [[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 %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: [[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 %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()