| ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py |
| ; RUN: llc --mtriple=loongarch32 -mattr=+32s,+d < %s | FileCheck %s --check-prefixes=CHECK,LA32 |
| ; RUN: llc --mtriple=loongarch64 -mattr=+d < %s | FileCheck %s --check-prefixes=CHECK,LA64 |
| |
| ;; Part 1: add — commutative, 2 (shared-operand side) x 2 (arm) = 4 |
| ;; Expect: single maskeqz/masknez + add (2 insns for select+add). |
| |
| define i64 @add_lhs_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_lhs_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: add.w $a3, $a1, $a3 |
| ; LA32-NEXT: add.w $a6, $a0, $a2 |
| ; LA32-NEXT: sltu $a6, $a6, $a0 |
| ; LA32-NEXT: add.w $a3, $a3, $a6 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a3, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a3, $a1 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: add.w $a0, $a0, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_lhs_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: add.d $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| ret i64 %2 |
| } |
| |
| define i64 @add_rhs_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_rhs_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: add.w $a1, $a1, $a3 |
| ; LA32-NEXT: add.w $a6, $a0, $a2 |
| ; LA32-NEXT: sltu $a6, $a6, $a0 |
| ; LA32-NEXT: add.w $a1, $a1, $a6 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a4 |
| ; LA32-NEXT: masknez $a3, $a3, $a4 |
| ; LA32-NEXT: or $a1, $a1, $a3 |
| ; LA32-NEXT: maskeqz $a0, $a0, $a4 |
| ; LA32-NEXT: add.w $a0, $a2, $a0 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_rhs_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a0, $a0, $a2 |
| ; LA64-NEXT: add.d $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %b |
| ret i64 %2 |
| } |
| |
| define i64 @add_lhs_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_lhs_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: add.w $a3, $a1, $a3 |
| ; LA32-NEXT: add.w $a6, $a0, $a2 |
| ; LA32-NEXT: sltu $a6, $a6, $a0 |
| ; LA32-NEXT: add.w $a3, $a3, $a6 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a3, $a3, $a4 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a1, $a3 |
| ; LA32-NEXT: masknez $a2, $a2, $a4 |
| ; LA32-NEXT: add.w $a0, $a0, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_lhs_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a1, $a1, $a2 |
| ; LA64-NEXT: add.d $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| define i64 @add_rhs_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_rhs_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: add.w $a1, $a1, $a3 |
| ; LA32-NEXT: add.w $a6, $a0, $a2 |
| ; LA32-NEXT: sltu $a6, $a6, $a0 |
| ; LA32-NEXT: add.w $a1, $a1, $a6 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: maskeqz $a3, $a3, $a4 |
| ; LA32-NEXT: or $a1, $a3, $a1 |
| ; LA32-NEXT: masknez $a0, $a0, $a4 |
| ; LA32-NEXT: add.w $a0, $a2, $a0 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_rhs_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a0, $a0, $a2 |
| ; LA64-NEXT: add.d $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %b, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Part 2: or / xor — same 4 combos each, commutative like add. |
| |
| define i64 @or_lhs_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: or_lhs_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: or $a0, $a0, $a2 |
| ; LA32-NEXT: maskeqz $a2, $a3, $a4 |
| ; LA32-NEXT: or $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: or_lhs_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: or $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = or i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| ret i64 %2 |
| } |
| |
| define i64 @or_rhs_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: or_rhs_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a0, $a0, $a4 |
| ; LA32-NEXT: or $a0, $a2, $a0 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a3, $a1 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: or_rhs_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a0, $a0, $a2 |
| ; LA64-NEXT: or $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = or i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %b |
| ret i64 %2 |
| } |
| |
| define i64 @or_lhs_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: or_lhs_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a2, $a2, $a4 |
| ; LA32-NEXT: or $a0, $a0, $a2 |
| ; LA32-NEXT: masknez $a2, $a3, $a4 |
| ; LA32-NEXT: or $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: or_lhs_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a1, $a1, $a2 |
| ; LA64-NEXT: or $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = or i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| define i64 @or_rhs_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: or_rhs_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a0, $a0, $a4 |
| ; LA32-NEXT: or $a0, $a2, $a0 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a3, $a1 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: or_rhs_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a0, $a0, $a2 |
| ; LA64-NEXT: or $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = or i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %b, i64 %0 |
| ret i64 %2 |
| } |
| |
| define i64 @xor_lhs_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: xor_lhs_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: xor $a0, $a0, $a2 |
| ; LA32-NEXT: maskeqz $a2, $a3, $a4 |
| ; LA32-NEXT: xor $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: xor_lhs_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: xor $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = xor i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| ret i64 %2 |
| } |
| |
| define i64 @xor_rhs_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: xor_rhs_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a0, $a0, $a4 |
| ; LA32-NEXT: xor $a0, $a2, $a0 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a4 |
| ; LA32-NEXT: xor $a1, $a3, $a1 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: xor_rhs_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a0, $a0, $a2 |
| ; LA64-NEXT: xor $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = xor i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %b |
| ret i64 %2 |
| } |
| |
| define i64 @xor_lhs_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: xor_lhs_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a2, $a2, $a4 |
| ; LA32-NEXT: xor $a0, $a0, $a2 |
| ; LA32-NEXT: masknez $a2, $a3, $a4 |
| ; LA32-NEXT: xor $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: xor_lhs_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a1, $a1, $a2 |
| ; LA64-NEXT: xor $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = xor i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| define i64 @xor_rhs_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: xor_rhs_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a0, $a0, $a4 |
| ; LA32-NEXT: xor $a0, $a2, $a0 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: xor $a1, $a3, $a1 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: xor_rhs_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a0, $a0, $a2 |
| ; LA64-NEXT: xor $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = xor i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %b, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Part 3: sub — NOT commutative. |
| ;; Positive: shared operand is the minuend (1st operand of sub). |
| ;; Negative: shared operand is the subtrahend (2nd operand of sub). |
| ;; MUST NOT be optimized (must keep maskeqz/masknez+or, 3 insns). |
| |
| ;; Positive: a - b, a shared -> should fold to: sub a, (select cond, b, 0) |
| define i64 @sub_minuend_true(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_minuend_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: sltu $a6, $a0, $a2 |
| ; LA32-NEXT: sub.w $a3, $a1, $a3 |
| ; LA32-NEXT: sub.w $a3, $a3, $a6 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a3, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a3, $a1 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: sub.w $a0, $a0, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_minuend_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: sub.d $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| ret i64 %2 |
| } |
| |
| define i64 @sub_minuend_false(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_minuend_false: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: sltu $a6, $a0, $a2 |
| ; LA32-NEXT: sub.w $a3, $a1, $a3 |
| ; LA32-NEXT: sub.w $a3, $a3, $a6 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a3, $a3, $a4 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a1, $a3 |
| ; LA32-NEXT: masknez $a2, $a2, $a4 |
| ; LA32-NEXT: sub.w $a0, $a0, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_minuend_false: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a1, $a1, $a2 |
| ; LA64-NEXT: sub.d $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Negative: b - a, a is the subtrahend (2nd operand). Cannot be rewritten as |
| ;; `a op (select ...)`. Must NOT be folded; must still emit |
| ;; maskeqz/masknez + or (3 instructions). |
| define i64 @sub_subtrahend_true_noopt(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_subtrahend_true_noopt: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: sltu $a6, $a2, $a0 |
| ; LA32-NEXT: sub.w $a3, $a3, $a1 |
| ; LA32-NEXT: sub.w $a3, $a3, $a6 |
| ; LA32-NEXT: sub.w $a2, $a2, $a0 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: masknez $a0, $a0, $a4 |
| ; LA32-NEXT: or $a0, $a2, $a0 |
| ; LA32-NEXT: maskeqz $a2, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a2, $a1 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_subtrahend_true_noopt: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: sub.d $a1, $a1, $a0 |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: masknez $a0, $a0, $a2 |
| ; LA64-NEXT: or $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %b, %a |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| ret i64 %2 |
| } |
| |
| define i64 @sub_subtrahend_false_noopt(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_subtrahend_false_noopt: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: sltu $a6, $a2, $a0 |
| ; LA32-NEXT: sub.w $a3, $a3, $a1 |
| ; LA32-NEXT: sub.w $a3, $a3, $a6 |
| ; LA32-NEXT: sub.w $a2, $a2, $a0 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: masknez $a2, $a2, $a4 |
| ; LA32-NEXT: maskeqz $a0, $a0, $a4 |
| ; LA32-NEXT: or $a0, $a0, $a2 |
| ; LA32-NEXT: masknez $a2, $a3, $a4 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_subtrahend_false_noopt: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: sub.d $a1, $a1, $a0 |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: masknez $a1, $a1, $a2 |
| ; LA64-NEXT: maskeqz $a0, $a0, $a2 |
| ; LA64-NEXT: or $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %b, %a |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Part 4: negative cases unrelated to the sub-direction issue. |
| |
| ;; Unsupported opcode (mul is not add/sub/or/xor) -> must NOT be folded. |
| define i64 @mul_noopt(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: mul_noopt: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: mul.w $a3, $a0, $a3 |
| ; LA32-NEXT: mulh.wu $a6, $a0, $a2 |
| ; LA32-NEXT: add.w $a3, $a6, $a3 |
| ; LA32-NEXT: mul.w $a6, $a1, $a2 |
| ; LA32-NEXT: add.w $a3, $a3, $a6 |
| ; LA32-NEXT: mul.w $a2, $a0, $a2 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: masknez $a0, $a0, $a4 |
| ; LA32-NEXT: or $a0, $a2, $a0 |
| ; LA32-NEXT: maskeqz $a2, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a2, $a1 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: mul_noopt: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: mul.d $a1, $a0, $a1 |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: masknez $a0, $a0, $a2 |
| ; LA64-NEXT: or $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = mul i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| ret i64 %2 |
| } |
| |
| ;; binop operands are unrelated to the other select arm (%e is unrelated to |
| ;; add(a,b)) -> must NOT be folded. |
| define i64 @add_unrelated_noopt(i64 %a, i64 %b, i64 %e, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_unrelated_noopt: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: add.w $a1, $a1, $a3 |
| ; LA32-NEXT: add.w $a2, $a0, $a2 |
| ; LA32-NEXT: sltu $a0, $a2, $a0 |
| ; LA32-NEXT: add.w $a1, $a1, $a0 |
| ; LA32-NEXT: slt $a3, $a6, $a7 |
| ; LA32-NEXT: maskeqz $a0, $a2, $a3 |
| ; LA32-NEXT: masknez $a2, $a4, $a3 |
| ; LA32-NEXT: or $a0, $a0, $a2 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a3 |
| ; LA32-NEXT: masknez $a2, $a5, $a3 |
| ; LA32-NEXT: or $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_unrelated_noopt: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: add.d $a0, $a0, $a1 |
| ; LA64-NEXT: slt $a1, $a3, $a4 |
| ; LA64-NEXT: maskeqz $a0, $a0, $a1 |
| ; LA64-NEXT: masknez $a1, $a2, $a1 |
| ; LA64-NEXT: or $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %e |
| ret i64 %2 |
| } |
| |
| ;; Part 5: multi-use — only representative for add/sub, mechanism identical |
| ;; for or/xor. Original binop result must still be computed once and |
| ;; reused; select part should still shrink to a single mask instruction. |
| |
| define i64 @add_lhs_true_multi_use(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_lhs_true_multi_use: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: add.w $a3, $a1, $a3 |
| ; LA32-NEXT: add.w $a6, $a0, $a2 |
| ; LA32-NEXT: sltu $a7, $a6, $a0 |
| ; LA32-NEXT: add.w $a3, $a3, $a7 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a5, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a5, $a1 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: add.w $a0, $a0, $a2 |
| ; LA32-NEXT: add.w $a1, $a3, $a1 |
| ; LA32-NEXT: add.w $a0, $a6, $a0 |
| ; LA32-NEXT: sltu $a2, $a0, $a6 |
| ; LA32-NEXT: add.w $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_lhs_true_multi_use: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: add.d $a4, $a0, $a1 |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: add.d $a0, $a0, $a1 |
| ; LA64-NEXT: add.d $a0, $a4, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| %3 = add i64 %0, %2 |
| ret i64 %3 |
| } |
| |
| define i64 @sub_minuend_true_multi_use(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_minuend_true_multi_use: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: sltu $a6, $a0, $a2 |
| ; LA32-NEXT: sub.w $a3, $a1, $a3 |
| ; LA32-NEXT: sub.w $a3, $a3, $a6 |
| ; LA32-NEXT: sub.w $a6, $a0, $a2 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a5, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a5, $a1 |
| ; LA32-NEXT: maskeqz $a2, $a2, $a4 |
| ; LA32-NEXT: sub.w $a0, $a0, $a2 |
| ; LA32-NEXT: add.w $a1, $a3, $a1 |
| ; LA32-NEXT: add.w $a0, $a6, $a0 |
| ; LA32-NEXT: sltu $a2, $a0, $a6 |
| ; LA32-NEXT: add.w $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_minuend_true_multi_use: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: sub.d $a4, $a0, $a1 |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA64-NEXT: sub.d $a0, $a0, $a1 |
| ; LA64-NEXT: add.d $a0, $a4, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| %3 = add i64 %0, %2 |
| ret i64 %3 |
| } |
| |
| ;; multi-use negative sub case: must still not be folded even with reuse. |
| define i64 @sub_subtrahend_true_noopt_multi_use(i64 %a, i64 %b, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_subtrahend_true_noopt_multi_use: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: sltu $a6, $a2, $a0 |
| ; LA32-NEXT: sub.w $a3, $a3, $a1 |
| ; LA32-NEXT: sub.w $a3, $a3, $a6 |
| ; LA32-NEXT: sub.w $a2, $a2, $a0 |
| ; LA32-NEXT: slt $a4, $a4, $a5 |
| ; LA32-NEXT: maskeqz $a5, $a2, $a4 |
| ; LA32-NEXT: masknez $a0, $a0, $a4 |
| ; LA32-NEXT: or $a0, $a5, $a0 |
| ; LA32-NEXT: maskeqz $a5, $a3, $a4 |
| ; LA32-NEXT: masknez $a1, $a1, $a4 |
| ; LA32-NEXT: or $a1, $a5, $a1 |
| ; LA32-NEXT: add.w $a1, $a3, $a1 |
| ; LA32-NEXT: add.w $a0, $a2, $a0 |
| ; LA32-NEXT: sltu $a2, $a0, $a2 |
| ; LA32-NEXT: add.w $a1, $a1, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_subtrahend_true_noopt_multi_use: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: sub.d $a1, $a1, $a0 |
| ; LA64-NEXT: slt $a2, $a2, $a3 |
| ; LA64-NEXT: maskeqz $a3, $a1, $a2 |
| ; LA64-NEXT: masknez $a0, $a0, $a2 |
| ; LA64-NEXT: or $a0, $a3, $a0 |
| ; LA64-NEXT: add.d $a0, $a1, $a0 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %b, %a |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %0, i64 %a |
| %3 = add i64 %0, %2 |
| ret i64 %3 |
| } |
| |
| ;; Part 6: immediate operand variants (also exercise add/sub with a |
| ;; constant "Y" operand). Kept with `icmp slt i32 %c, %d` to isolate |
| ;; from the unrelated SETCC-immediate combine. |
| |
| define i64 @add_imm_true(i64 %a, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: add_imm_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: addi.w $a4, $a0, 5 |
| ; LA32-NEXT: sltu $a4, $a4, $a0 |
| ; LA32-NEXT: slt $a2, $a2, $a3 |
| ; LA32-NEXT: masknez $a3, $a4, $a2 |
| ; LA32-NEXT: add.w $a1, $a1, $a3 |
| ; LA32-NEXT: ori $a3, $zero, 5 |
| ; LA32-NEXT: masknez $a2, $a3, $a2 |
| ; LA32-NEXT: add.w $a0, $a0, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: add_imm_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a1, $a1, $a2 |
| ; LA64-NEXT: ori $a2, $zero, 5 |
| ; LA64-NEXT: masknez $a1, $a2, $a1 |
| ; LA64-NEXT: add.d $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = add i64 %a, 5 |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Positive: a - 5, a is minuend -> fold. |
| define i64 @sub_imm_minuend_true(i64 %a, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_imm_minuend_true: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: addi.w $a4, $a0, -5 |
| ; LA32-NEXT: sltu $a4, $a4, $a0 |
| ; LA32-NEXT: add.w $a4, $a1, $a4 |
| ; LA32-NEXT: addi.w $a4, $a4, -1 |
| ; LA32-NEXT: slt $a2, $a2, $a3 |
| ; LA32-NEXT: masknez $a3, $a4, $a2 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA32-NEXT: or $a1, $a1, $a3 |
| ; LA32-NEXT: addi.w $a3, $zero, -5 |
| ; LA32-NEXT: masknez $a2, $a3, $a2 |
| ; LA32-NEXT: add.w $a0, $a0, $a2 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_imm_minuend_true: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: slt $a1, $a1, $a2 |
| ; LA64-NEXT: addi.w $a2, $zero, -5 |
| ; LA64-NEXT: masknez $a1, $a2, $a1 |
| ; LA64-NEXT: add.d $a0, $a0, $a1 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 %a, 5 |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Negative: 5 - a, a is subtrahend -> must NOT fold. |
| define i64 @sub_imm_subtrahend_noopt(i64 %a, i32 signext %c, i32 signext %d) nounwind { |
| ; LA32-LABEL: sub_imm_subtrahend_noopt: |
| ; LA32: # %bb.0: # %entry |
| ; LA32-NEXT: ori $a4, $zero, 5 |
| ; LA32-NEXT: sltu $a5, $a4, $a0 |
| ; LA32-NEXT: add.w $a5, $a1, $a5 |
| ; LA32-NEXT: sub.w $a5, $zero, $a5 |
| ; LA32-NEXT: sub.w $a4, $a4, $a0 |
| ; LA32-NEXT: slt $a2, $a2, $a3 |
| ; LA32-NEXT: masknez $a3, $a4, $a2 |
| ; LA32-NEXT: maskeqz $a0, $a0, $a2 |
| ; LA32-NEXT: or $a0, $a0, $a3 |
| ; LA32-NEXT: masknez $a3, $a5, $a2 |
| ; LA32-NEXT: maskeqz $a1, $a1, $a2 |
| ; LA32-NEXT: or $a1, $a1, $a3 |
| ; LA32-NEXT: ret |
| ; |
| ; LA64-LABEL: sub_imm_subtrahend_noopt: |
| ; LA64: # %bb.0: # %entry |
| ; LA64-NEXT: ori $a3, $zero, 5 |
| ; LA64-NEXT: sub.d $a3, $a3, $a0 |
| ; LA64-NEXT: slt $a1, $a1, $a2 |
| ; LA64-NEXT: masknez $a2, $a3, $a1 |
| ; LA64-NEXT: maskeqz $a0, $a0, $a1 |
| ; LA64-NEXT: or $a0, $a0, $a2 |
| ; LA64-NEXT: ret |
| entry: |
| %0 = sub i64 5, %a |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i64 %a, i64 %0 |
| ret i64 %2 |
| } |
| |
| ;; Part 7: LA32 (i32 GRLen) sanity — same logic must hold for 32-bit. |
| |
| define signext i32 @add_lhs_true_i32(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d) nounwind { |
| ; CHECK-LABEL: add_lhs_true_i32: |
| ; CHECK: # %bb.0: # %entry |
| ; CHECK-NEXT: slt $a2, $a2, $a3 |
| ; CHECK-NEXT: maskeqz $a1, $a1, $a2 |
| ; CHECK-NEXT: add.w $a0, $a0, $a1 |
| ; CHECK-NEXT: ret |
| entry: |
| %0 = add i32 %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i32 %0, i32 %a |
| ret i32 %2 |
| } |
| |
| ;; Negative sub case must also hold on i32. |
| define signext i32 @sub_subtrahend_true_noopt_i32(i32 signext %a, i32 signext %b, i32 signext %c, i32 signext %d) nounwind { |
| ; CHECK-LABEL: sub_subtrahend_true_noopt_i32: |
| ; CHECK: # %bb.0: # %entry |
| ; CHECK-NEXT: sub.w $a1, $a1, $a0 |
| ; CHECK-NEXT: slt $a2, $a2, $a3 |
| ; CHECK-NEXT: maskeqz $a1, $a1, $a2 |
| ; CHECK-NEXT: masknez $a0, $a0, $a2 |
| ; CHECK-NEXT: or $a0, $a1, $a0 |
| ; CHECK-NEXT: ret |
| entry: |
| %0 = sub i32 %b, %a |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, i32 %0, i32 %a |
| ret i32 %2 |
| } |
| |
| ;; Part 8: types outside the combine's scope must be unaffected. |
| |
| ;; Float select: VT is f64, not GRLenVT -> must be unaffected by this combine. |
| define double @fadd_select_unaffected(double %a, double %b, i32 signext %c, i32 signext %d) nounwind { |
| ; CHECK-LABEL: fadd_select_unaffected: |
| ; CHECK: # %bb.0: # %entry |
| ; CHECK-NEXT: slt $a0, $a0, $a1 |
| ; CHECK-NEXT: fadd.d $fa1, $fa0, $fa1 |
| ; CHECK-NEXT: movgr2cf $fcc0, $a0 |
| ; CHECK-NEXT: fsel $fa0, $fa0, $fa1, $fcc0 |
| ; CHECK-NEXT: ret |
| entry: |
| %0 = fadd double %a, %b |
| %1 = icmp slt i32 %c, %d |
| %2 = select i1 %1, double %0, double %a |
| ret double %2 |
| } |