llvm/llvm/test/Transforms/LICM/hoist-binop.ll

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

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

; 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()