diff --git a/llvm/test/CodeGen/RISCV/selectcc-to-shiftand.ll b/llvm/test/CodeGen/RISCV/selectcc-to-shiftand.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/selectcc-to-shiftand.ll @@ -0,0 +1,252 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=riscv32 \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV32,RV32I +; RUN: llc < %s -mtriple=riscv64 \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV64,RV64I +; RUN: llc < %s -mtriple=riscv32 -mattr=+experimental-zbb \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV32,RV32ZBB +; RUN: llc < %s -mtriple=riscv64 -mattr=+experimental-zbb \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV64,RV64ZBB + +; Compare if negative and select of constants where one constant is zero. +define i32 @neg_sel_constants(i32 signext %a) { +; RV32-LABEL: neg_sel_constants: +; RV32: # %bb.0: +; RV32-NEXT: srai a0, a0, 31 +; RV32-NEXT: andi a0, a0, 5 +; RV32-NEXT: ret +; +; RV64-LABEL: neg_sel_constants: +; RV64: # %bb.0: +; RV64-NEXT: srai a0, a0, 63 +; RV64-NEXT: andi a0, a0, 5 +; RV64-NEXT: ret + %tmp.1 = icmp slt i32 %a, 0 + %retval = select i1 %tmp.1, i32 5, i32 0 + ret i32 %retval +} + +; Compare if negative and select of constants where one constant is zero and the +; other is a single bit. +define i32 @neg_sel_special_constant(i32 signext %a) { +; RV32-LABEL: neg_sel_special_constant: +; RV32: # %bb.0: +; RV32-NEXT: srli a0, a0, 22 +; RV32-NEXT: andi a0, a0, 512 +; RV32-NEXT: ret +; +; RV64-LABEL: neg_sel_special_constant: +; RV64: # %bb.0: +; RV64-NEXT: addi a1, zero, 1 +; RV64-NEXT: slli a1, a1, 31 +; RV64-NEXT: and a0, a0, a1 +; RV64-NEXT: srli a0, a0, 22 +; RV64-NEXT: ret + %tmp.1 = icmp slt i32 %a, 0 + %retval = select i1 %tmp.1, i32 512, i32 0 + ret i32 %retval +} + +; Compare if negative and select variable or zero. +define i32 @neg_sel_variable_and_zero(i32 signext %a, i32 signext %b) { +; CHECK-LABEL: neg_sel_variable_and_zero: +; CHECK: # %bb.0: +; CHECK-NEXT: srai a0, a0, 31 +; CHECK-NEXT: and a0, a0, a1 +; CHECK-NEXT: ret + %tmp.1 = icmp slt i32 %a, 0 + %retval = select i1 %tmp.1, i32 %b, i32 0 + ret i32 %retval +} + +; Compare if not positive and select the same variable as being compared: +; smin(a, 0). +define i32 @not_pos_sel_same_variable(i32 signext %a) { +; CHECK-LABEL: not_pos_sel_same_variable: +; CHECK: # %bb.0: +; CHECK-NEXT: srai a1, a0, 31 +; CHECK-NEXT: and a0, a1, a0 +; CHECK-NEXT: ret + %tmp = icmp slt i32 %a, 1 + %min = select i1 %tmp, i32 %a, i32 0 + ret i32 %min +} + +; Flipping the comparison condition can be handled by getting the bitwise not of +; the sign mask. +; TODO: We aren't doing a good job of this. + +; Compare if positive and select of constants where one constant is zero. +define i32 @pos_sel_constants(i32 signext %a) { +; CHECK-LABEL: pos_sel_constants: +; CHECK: # %bb.0: +; CHECK-NEXT: mv a1, a0 +; CHECK-NEXT: addi a0, zero, 5 +; CHECK-NEXT: bgez a1, .LBB4_2 +; CHECK-NEXT: # %bb.1: +; CHECK-NEXT: mv a0, zero +; CHECK-NEXT: .LBB4_2: +; CHECK-NEXT: ret + %tmp.1 = icmp sgt i32 %a, -1 + %retval = select i1 %tmp.1, i32 5, i32 0 + ret i32 %retval +} + +; Compare if positive and select of constants where one constant is zero and the +; other is a single bit. +; TODO: Why do RV32 and RV64 generate different code? RV64 uses more registers, +; but the addi isn't part of the dependency chain of %a so may be faster. +define i32 @pos_sel_special_constant(i32 signext %a) { +; RV32-LABEL: pos_sel_special_constant: +; RV32: # %bb.0: +; RV32-NEXT: not a0, a0 +; RV32-NEXT: srli a0, a0, 22 +; RV32-NEXT: andi a0, a0, 512 +; RV32-NEXT: ret +; +; RV64-LABEL: pos_sel_special_constant: +; RV64: # %bb.0: +; RV64-NEXT: addi a1, zero, -1 +; RV64-NEXT: slt a0, a1, a0 +; RV64-NEXT: slli a0, a0, 9 +; RV64-NEXT: ret + %tmp.1 = icmp sgt i32 %a, -1 + %retval = select i1 %tmp.1, i32 512, i32 0 + ret i32 %retval +} + +; Compare if positive and select variable or zero. +define i32 @pos_sel_variable_and_zero(i32 signext %a, i32 signext %b) { +; CHECK-LABEL: pos_sel_variable_and_zero: +; CHECK: # %bb.0: +; CHECK-NEXT: bgez a0, .LBB6_2 +; CHECK-NEXT: # %bb.1: +; CHECK-NEXT: mv a1, zero +; CHECK-NEXT: .LBB6_2: +; CHECK-NEXT: mv a0, a1 +; CHECK-NEXT: ret + %tmp.1 = icmp sgt i32 %a, -1 + %retval = select i1 %tmp.1, i32 %b, i32 0 + ret i32 %retval +} + +; Compare if not negative or zero and select the same variable as being +; compared: smax(a, 0). +define i32 @not_neg_sel_same_variable(i32 signext %a) { +; RV32I-LABEL: not_neg_sel_same_variable: +; RV32I: # %bb.0: +; RV32I-NEXT: bgtz a0, .LBB7_2 +; RV32I-NEXT: # %bb.1: +; RV32I-NEXT: mv a0, zero +; RV32I-NEXT: .LBB7_2: +; RV32I-NEXT: ret +; +; RV64I-LABEL: not_neg_sel_same_variable: +; RV64I: # %bb.0: +; RV64I-NEXT: bgtz a0, .LBB7_2 +; RV64I-NEXT: # %bb.1: +; RV64I-NEXT: mv a0, zero +; RV64I-NEXT: .LBB7_2: +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: not_neg_sel_same_variable: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: max a0, a0, zero +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: not_neg_sel_same_variable: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: max a0, a0, zero +; RV64ZBB-NEXT: ret + %tmp = icmp sgt i32 %a, 0 + %min = select i1 %tmp, i32 %a, i32 0 + ret i32 %min +} + +; ret = (x-y) > 0 ? x-y : 0 +define i32 @sub_clamp_zero(i32 signext %x, i32 signext %y) { +; RV32I-LABEL: sub_clamp_zero: +; RV32I: # %bb.0: +; RV32I-NEXT: sub a0, a0, a1 +; RV32I-NEXT: bgtz a0, .LBB8_2 +; RV32I-NEXT: # %bb.1: +; RV32I-NEXT: mv a0, zero +; RV32I-NEXT: .LBB8_2: +; RV32I-NEXT: ret +; +; RV64I-LABEL: sub_clamp_zero: +; RV64I: # %bb.0: +; RV64I-NEXT: subw a0, a0, a1 +; RV64I-NEXT: bgtz a0, .LBB8_2 +; RV64I-NEXT: # %bb.1: +; RV64I-NEXT: mv a0, zero +; RV64I-NEXT: .LBB8_2: +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: sub_clamp_zero: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: sub a0, a0, a1 +; RV32ZBB-NEXT: max a0, a0, zero +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: sub_clamp_zero: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: subw a0, a0, a1 +; RV64ZBB-NEXT: max a0, a0, zero +; RV64ZBB-NEXT: ret + %sub = sub nsw i32 %x, %y + %cmp = icmp sgt i32 %sub, 0 + %sel = select i1 %cmp, i32 %sub, i32 0 + ret i32 %sel +} + +define i8 @sel_shift_bool_i8(i1 %t) { +; CHECK-LABEL: sel_shift_bool_i8: +; CHECK: # %bb.0: +; CHECK-NEXT: andi a1, a0, 1 +; CHECK-NEXT: addi a0, zero, -128 +; CHECK-NEXT: bnez a1, .LBB9_2 +; CHECK-NEXT: # %bb.1: +; CHECK-NEXT: mv a0, zero +; CHECK-NEXT: .LBB9_2: +; CHECK-NEXT: ret + %shl = select i1 %t, i8 128, i8 0 + ret i8 %shl +} + +define i16 @sel_shift_bool_i16(i1 %t) { +; CHECK-LABEL: sel_shift_bool_i16: +; CHECK: # %bb.0: +; CHECK-NEXT: andi a0, a0, 1 +; CHECK-NEXT: slli a0, a0, 7 +; CHECK-NEXT: ret + %shl = select i1 %t, i16 128, i16 0 + ret i16 %shl +} + +define i32 @sel_shift_bool_i32(i1 %t) { +; CHECK-LABEL: sel_shift_bool_i32: +; CHECK: # %bb.0: +; CHECK-NEXT: andi a0, a0, 1 +; CHECK-NEXT: slli a0, a0, 6 +; CHECK-NEXT: ret + %shl = select i1 %t, i32 64, i32 0 + ret i32 %shl +} + +define i64 @sel_shift_bool_i64(i1 %t) { +; RV32-LABEL: sel_shift_bool_i64: +; RV32: # %bb.0: +; RV32-NEXT: andi a0, a0, 1 +; RV32-NEXT: slli a0, a0, 16 +; RV32-NEXT: mv a1, zero +; RV32-NEXT: ret +; +; RV64-LABEL: sel_shift_bool_i64: +; RV64: # %bb.0: +; RV64-NEXT: andi a0, a0, 1 +; RV64-NEXT: slli a0, a0, 16 +; RV64-NEXT: ret + %shl = select i1 %t, i64 65536, i64 0 + ret i64 %shl +} diff --git a/llvm/test/CodeGen/RISCV/unfold-masked-merge-scalar-variablemask.ll b/llvm/test/CodeGen/RISCV/unfold-masked-merge-scalar-variablemask.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/unfold-masked-merge-scalar-variablemask.ll @@ -0,0 +1,1113 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=riscv32 < %s \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV32,RV32I +; RUN: llc -mtriple=riscv64 < %s \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV64,RV64I +; RUN: llc -mtriple=riscv32 -mattr=+experimental-zbb < %s \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV32,RV32ZBB +; RUN: llc -mtriple=riscv64 -mattr=+experimental-zbb < %s \ +; RUN: | FileCheck %s --check-prefixes=CHECK,RV64,RV64ZBB + +define i8 @out8(i8 %x, i8 %y, i8 %mask) { +; RV32I-LABEL: out8: +; RV32I: # %bb.0: +; RV32I-NEXT: and a0, a0, a2 +; RV32I-NEXT: not a2, a2 +; RV32I-NEXT: and a1, a1, a2 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out8: +; RV64I: # %bb.0: +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: not a2, a2 +; RV64I-NEXT: and a1, a1, a2 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out8: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a0, a2 +; RV32ZBB-NEXT: andn a1, a1, a2 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out8: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a0, a2 +; RV64ZBB-NEXT: andn a1, a1, a2 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %mx = and i8 %x, %mask + %notmask = xor i8 %mask, -1 + %my = and i8 %y, %notmask + %r = or i8 %mx, %my + ret i8 %r +} + +define i16 @out16(i16 %x, i16 %y, i16 %mask) { +; RV32I-LABEL: out16: +; RV32I: # %bb.0: +; RV32I-NEXT: and a0, a0, a2 +; RV32I-NEXT: not a2, a2 +; RV32I-NEXT: and a1, a1, a2 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out16: +; RV64I: # %bb.0: +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: not a2, a2 +; RV64I-NEXT: and a1, a1, a2 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out16: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a0, a2 +; RV32ZBB-NEXT: andn a1, a1, a2 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out16: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a0, a2 +; RV64ZBB-NEXT: andn a1, a1, a2 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %mx = and i16 %x, %mask + %notmask = xor i16 %mask, -1 + %my = and i16 %y, %notmask + %r = or i16 %mx, %my + ret i16 %r +} + +define i32 @out32(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out32: +; RV32I: # %bb.0: +; RV32I-NEXT: and a0, a0, a2 +; RV32I-NEXT: not a2, a2 +; RV32I-NEXT: and a1, a1, a2 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out32: +; RV64I: # %bb.0: +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: not a2, a2 +; RV64I-NEXT: and a1, a1, a2 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out32: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a0, a2 +; RV32ZBB-NEXT: andn a1, a1, a2 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out32: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a0, a2 +; RV64ZBB-NEXT: andn a1, a1, a2 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %mx = and i32 %x, %mask + %notmask = xor i32 %mask, -1 + %my = and i32 %y, %notmask + %r = or i32 %mx, %my + ret i32 %r +} + +define i64 @out64(i64 %x, i64 %y, i64 %mask) { +; RV32I-LABEL: out64: +; RV32I: # %bb.0: +; RV32I-NEXT: and a1, a1, a5 +; RV32I-NEXT: and a0, a0, a4 +; RV32I-NEXT: not a4, a4 +; RV32I-NEXT: not a5, a5 +; RV32I-NEXT: and a3, a3, a5 +; RV32I-NEXT: and a2, a2, a4 +; RV32I-NEXT: or a0, a0, a2 +; RV32I-NEXT: or a1, a1, a3 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out64: +; RV64I: # %bb.0: +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: not a2, a2 +; RV64I-NEXT: and a1, a1, a2 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out64: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a1, a1, a5 +; RV32ZBB-NEXT: and a0, a0, a4 +; RV32ZBB-NEXT: andn a3, a3, a5 +; RV32ZBB-NEXT: andn a2, a2, a4 +; RV32ZBB-NEXT: or a0, a0, a2 +; RV32ZBB-NEXT: or a1, a1, a3 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out64: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a0, a2 +; RV64ZBB-NEXT: andn a1, a1, a2 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %mx = and i64 %x, %mask + %notmask = xor i64 %mask, -1 + %my = and i64 %y, %notmask + %r = or i64 %mx, %my + ret i64 %r +} + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; TODO: These tests should produce the same output as the corresponding out* test. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +define i8 @in8(i8 %x, i8 %y, i8 %mask) { +; CHECK-LABEL: in8: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i8 %x, %y + %n1 = and i8 %n0, %mask + %r = xor i8 %n1, %y + ret i8 %r +} + +define i16 @in16(i16 %x, i16 %y, i16 %mask) { +; CHECK-LABEL: in16: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i16 %x, %y + %n1 = and i16 %n0, %mask + %r = xor i16 %n1, %y + ret i16 %r +} + +define i32 @in32(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in32: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %y + ret i32 %r +} + +define i64 @in64(i64 %x, i64 %y, i64 %mask) { +; RV32-LABEL: in64: +; RV32: # %bb.0: +; RV32-NEXT: xor a0, a0, a2 +; RV32-NEXT: xor a1, a1, a3 +; RV32-NEXT: and a1, a1, a5 +; RV32-NEXT: and a0, a0, a4 +; RV32-NEXT: xor a0, a0, a2 +; RV32-NEXT: xor a1, a1, a3 +; RV32-NEXT: ret +; +; RV64-LABEL: in64: +; RV64: # %bb.0: +; RV64-NEXT: xor a0, a0, a1 +; RV64-NEXT: and a0, a0, a2 +; RV64-NEXT: xor a0, a0, a1 +; RV64-NEXT: ret + %n0 = xor i64 %x, %y + %n1 = and i64 %n0, %mask + %r = xor i64 %n1, %y + ret i64 %r +} + +; ============================================================================ ; +; Commutativity tests. +; ============================================================================ ; + +define i32 @in_commutativity_0_0_1(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_0_0_1: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a2, a0 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 ; swapped + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @in_commutativity_0_1_0(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_0_1_0: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %y, %n1 ; swapped + ret i32 %r +} + +define i32 @in_commutativity_0_1_1(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_0_1_1: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a2, a0 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 ; swapped + %r = xor i32 %y, %n1 ; swapped + ret i32 %r +} + +define i32 @in_commutativity_1_0_0(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_1_0_0: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a1, a0, a1 +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %x ; %x instead of %y + ret i32 %r +} + +define i32 @in_commutativity_1_0_1(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_1_0_1: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a1, a0, a1 +; CHECK-NEXT: and a1, a2, a1 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 ; swapped + %r = xor i32 %n1, %x ; %x instead of %y + ret i32 %r +} + +define i32 @in_commutativity_1_1_0(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_1_1_0: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a1, a0, a1 +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %x, %n1 ; swapped, %x instead of %y + ret i32 %r +} + +define i32 @in_commutativity_1_1_1(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_commutativity_1_1_1: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a1, a0, a1 +; CHECK-NEXT: and a1, a2, a1 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 ; swapped + %r = xor i32 %x, %n1 ; swapped, %x instead of %y + ret i32 %r +} + +; ============================================================================ ; +; Y is an 'and' too. +; ============================================================================ ; + +define i32 @in_complex_y0(i32 %x, i32 %y_hi, i32 %y_low, i32 %mask) { +; CHECK-LABEL: in_complex_y0: +; CHECK: # %bb.0: +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a3 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %y = and i32 %y_hi, %y_low + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @in_complex_y1(i32 %x, i32 %y_hi, i32 %y_low, i32 %mask) { +; CHECK-LABEL: in_complex_y1: +; CHECK: # %bb.0: +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a3 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %y = and i32 %y_hi, %y_low + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %y, %n1 + ret i32 %r +} + +; ============================================================================ ; +; M is an 'xor' too. +; ============================================================================ ; + +define i32 @in_complex_m0(i32 %x, i32 %y, i32 %m_a, i32 %m_b) { +; CHECK-LABEL: in_complex_m0: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a2, a2, a3 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %mask = xor i32 %m_a, %m_b + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @in_complex_m1(i32 %x, i32 %y, i32 %m_a, i32 %m_b) { +; CHECK-LABEL: in_complex_m1: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a2, a2, a3 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a2, a0 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %mask = xor i32 %m_a, %m_b + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 + %r = xor i32 %n1, %y + ret i32 %r +} + +; ============================================================================ ; +; Both Y and M are complex. +; ============================================================================ ; + +define i32 @in_complex_y0_m0(i32 %x, i32 %y_hi, i32 %y_low, i32 %m_a, i32 %m_b) { +; CHECK-LABEL: in_complex_y0_m0: +; CHECK: # %bb.0: +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a2, a3, a4 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %y = and i32 %y_hi, %y_low + %mask = xor i32 %m_a, %m_b + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @in_complex_y1_m0(i32 %x, i32 %y_hi, i32 %y_low, i32 %m_a, i32 %m_b) { +; CHECK-LABEL: in_complex_y1_m0: +; CHECK: # %bb.0: +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a2, a3, a4 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %y = and i32 %y_hi, %y_low + %mask = xor i32 %m_a, %m_b + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %y, %n1 + ret i32 %r +} + +define i32 @in_complex_y0_m1(i32 %x, i32 %y_hi, i32 %y_low, i32 %m_a, i32 %m_b) { +; CHECK-LABEL: in_complex_y0_m1: +; CHECK: # %bb.0: +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a2, a3, a4 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a2, a0 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %y = and i32 %y_hi, %y_low + %mask = xor i32 %m_a, %m_b + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @in_complex_y1_m1(i32 %x, i32 %y_hi, i32 %y_low, i32 %m_a, i32 %m_b) { +; CHECK-LABEL: in_complex_y1_m1: +; CHECK: # %bb.0: +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: xor a2, a3, a4 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a2, a0 +; CHECK-NEXT: xor a0, a1, a0 +; CHECK-NEXT: ret + %y = and i32 %y_hi, %y_low + %mask = xor i32 %m_a, %m_b + %n0 = xor i32 %x, %y + %n1 = and i32 %mask, %n0 + %r = xor i32 %y, %n1 + ret i32 %r +} + +; ============================================================================ ; +; Various cases with %x and/or %y being a constant +; ============================================================================ ; + +define i32 @out_constant_varx_mone(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out_constant_varx_mone: +; RV32I: # %bb.0: +; RV32I-NEXT: not a1, a2 +; RV32I-NEXT: and a0, a2, a0 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out_constant_varx_mone: +; RV64I: # %bb.0: +; RV64I-NEXT: not a1, a2 +; RV64I-NEXT: and a0, a2, a0 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out_constant_varx_mone: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a2, a0 +; RV32ZBB-NEXT: orn a0, a0, a2 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out_constant_varx_mone: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a2, a0 +; RV64ZBB-NEXT: orn a0, a0, a2 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %mask, %x + %my = and i32 %notmask, -1 + %r = or i32 %mx, %my + ret i32 %r +} + +define i32 @in_constant_varx_mone(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: in_constant_varx_mone: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a0 +; RV32I-NEXT: and a0, a0, a2 +; RV32I-NEXT: not a0, a0 +; RV32I-NEXT: ret +; +; RV64I-LABEL: in_constant_varx_mone: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a0 +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: not a0, a0 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: in_constant_varx_mone: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: andn a0, a2, a0 +; RV32ZBB-NEXT: not a0, a0 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: in_constant_varx_mone: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: andn a0, a2, a0 +; RV64ZBB-NEXT: not a0, a0 +; RV64ZBB-NEXT: ret + %n0 = xor i32 %x, -1 ; %x + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, -1 + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @out_constant_varx_mone_invmask(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: out_constant_varx_mone_invmask: +; CHECK: # %bb.0: +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %notmask, %x + %my = and i32 %mask, -1 + %r = or i32 %mx, %my + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @in_constant_varx_mone_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: in_constant_varx_mone_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a1, a2 +; RV32I-NEXT: not a0, a0 +; RV32I-NEXT: and a0, a0, a1 +; RV32I-NEXT: not a0, a0 +; RV32I-NEXT: ret +; +; RV64I-LABEL: in_constant_varx_mone_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a1, a2 +; RV64I-NEXT: not a0, a0 +; RV64I-NEXT: and a0, a0, a1 +; RV64I-NEXT: not a0, a0 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: in_constant_varx_mone_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: not a0, a0 +; RV32ZBB-NEXT: andn a0, a0, a2 +; RV32ZBB-NEXT: not a0, a0 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: in_constant_varx_mone_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: not a0, a0 +; RV64ZBB-NEXT: andn a0, a0, a2 +; RV64ZBB-NEXT: not a0, a0 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %n0 = xor i32 %x, -1 ; %x + %n1 = and i32 %n0, %notmask + %r = xor i32 %n1, -1 + ret i32 %r +} + +define i32 @out_constant_varx_42(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out_constant_varx_42: +; RV32I: # %bb.0: +; RV32I-NEXT: not a1, a2 +; RV32I-NEXT: and a0, a2, a0 +; RV32I-NEXT: andi a1, a1, 42 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out_constant_varx_42: +; RV64I: # %bb.0: +; RV64I-NEXT: not a1, a2 +; RV64I-NEXT: and a0, a2, a0 +; RV64I-NEXT: andi a1, a1, 42 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out_constant_varx_42: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a2, a0 +; RV32ZBB-NEXT: addi a1, zero, 42 +; RV32ZBB-NEXT: andn a1, a1, a2 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out_constant_varx_42: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a2, a0 +; RV64ZBB-NEXT: addi a1, zero, 42 +; RV64ZBB-NEXT: andn a1, a1, a2 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %mask, %x + %my = and i32 %notmask, 42 + %r = or i32 %mx, %my + ret i32 %r +} + +define i32 @in_constant_varx_42(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_constant_varx_42: +; CHECK: # %bb.0: +; CHECK-NEXT: xori a0, a0, 42 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xori a0, a0, 42 +; CHECK-NEXT: ret + %n0 = xor i32 %x, 42 ; %x + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, 42 + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @out_constant_varx_42_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out_constant_varx_42_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a1, a2 +; RV32I-NEXT: and a0, a1, a0 +; RV32I-NEXT: andi a1, a2, 42 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out_constant_varx_42_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a1, a2 +; RV64I-NEXT: and a0, a1, a0 +; RV64I-NEXT: andi a1, a2, 42 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out_constant_varx_42_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: andn a0, a0, a2 +; RV32ZBB-NEXT: andi a1, a2, 42 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out_constant_varx_42_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: andn a0, a0, a2 +; RV64ZBB-NEXT: andi a1, a2, 42 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %notmask, %x + %my = and i32 %mask, 42 + %r = or i32 %mx, %my + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @in_constant_varx_42_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: in_constant_varx_42_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a1, a2 +; RV32I-NEXT: xori a0, a0, 42 +; RV32I-NEXT: and a0, a0, a1 +; RV32I-NEXT: xori a0, a0, 42 +; RV32I-NEXT: ret +; +; RV64I-LABEL: in_constant_varx_42_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a1, a2 +; RV64I-NEXT: xori a0, a0, 42 +; RV64I-NEXT: and a0, a0, a1 +; RV64I-NEXT: xori a0, a0, 42 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: in_constant_varx_42_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: xori a0, a0, 42 +; RV32ZBB-NEXT: andn a0, a0, a2 +; RV32ZBB-NEXT: xori a0, a0, 42 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: in_constant_varx_42_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: xori a0, a0, 42 +; RV64ZBB-NEXT: andn a0, a0, a2 +; RV64ZBB-NEXT: xori a0, a0, 42 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %n0 = xor i32 %x, 42 ; %x + %n1 = and i32 %n0, %notmask + %r = xor i32 %n1, 42 + ret i32 %r +} + +define i32 @out_constant_mone_vary(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: out_constant_mone_vary: +; CHECK: # %bb.0: +; CHECK-NEXT: or a0, a1, a2 +; CHECK-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %mask, -1 + %my = and i32 %notmask, %y + %r = or i32 %mx, %my + ret i32 %r +} + +define i32 @in_constant_mone_vary(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: in_constant_mone_vary: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a1 +; RV32I-NEXT: and a0, a0, a2 +; RV32I-NEXT: xor a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: in_constant_mone_vary: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a1 +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: xor a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: in_constant_mone_vary: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: andn a0, a2, a1 +; RV32ZBB-NEXT: xor a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: in_constant_mone_vary: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: andn a0, a2, a1 +; RV64ZBB-NEXT: xor a0, a0, a1 +; RV64ZBB-NEXT: ret + %n0 = xor i32 -1, %y ; %x + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %y + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @out_constant_mone_vary_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out_constant_mone_vary_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a2 +; RV32I-NEXT: and a1, a2, a1 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out_constant_mone_vary_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a2 +; RV64I-NEXT: and a1, a2, a1 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out_constant_mone_vary_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a2, a1 +; RV32ZBB-NEXT: orn a0, a0, a2 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out_constant_mone_vary_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a2, a1 +; RV64ZBB-NEXT: orn a0, a0, a2 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %notmask, -1 + %my = and i32 %mask, %y + %r = or i32 %mx, %my + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @in_constant_mone_vary_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: in_constant_mone_vary_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a2 +; RV32I-NEXT: not a2, a1 +; RV32I-NEXT: and a0, a2, a0 +; RV32I-NEXT: xor a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: in_constant_mone_vary_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a2 +; RV64I-NEXT: not a2, a1 +; RV64I-NEXT: and a0, a2, a0 +; RV64I-NEXT: xor a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: in_constant_mone_vary_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: not a0, a1 +; RV32ZBB-NEXT: andn a0, a0, a2 +; RV32ZBB-NEXT: xor a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: in_constant_mone_vary_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: not a0, a1 +; RV64ZBB-NEXT: andn a0, a0, a2 +; RV64ZBB-NEXT: xor a0, a0, a1 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %n0 = xor i32 -1, %y ; %x + %n1 = and i32 %n0, %notmask + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @out_constant_42_vary(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out_constant_42_vary: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a2 +; RV32I-NEXT: andi a2, a2, 42 +; RV32I-NEXT: and a0, a0, a1 +; RV32I-NEXT: or a0, a2, a0 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out_constant_42_vary: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a2 +; RV64I-NEXT: andi a2, a2, 42 +; RV64I-NEXT: and a0, a0, a1 +; RV64I-NEXT: or a0, a2, a0 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out_constant_42_vary: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: andi a0, a2, 42 +; RV32ZBB-NEXT: andn a1, a1, a2 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out_constant_42_vary: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: andi a0, a2, 42 +; RV64ZBB-NEXT: andn a1, a1, a2 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %mask, 42 + %my = and i32 %notmask, %y + %r = or i32 %mx, %my + ret i32 %r +} + +define i32 @in_constant_42_vary(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: in_constant_42_vary: +; CHECK: # %bb.0: +; CHECK-NEXT: xori a0, a1, 42 +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: ret + %n0 = xor i32 42, %y ; %x + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %y + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @out_constant_42_vary_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: out_constant_42_vary_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a2 +; RV32I-NEXT: andi a0, a0, 42 +; RV32I-NEXT: and a1, a2, a1 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: out_constant_42_vary_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a2 +; RV64I-NEXT: andi a0, a0, 42 +; RV64I-NEXT: and a1, a2, a1 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: out_constant_42_vary_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: addi a0, zero, 42 +; RV32ZBB-NEXT: andn a0, a0, a2 +; RV32ZBB-NEXT: and a1, a2, a1 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: out_constant_42_vary_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: addi a0, zero, 42 +; RV64ZBB-NEXT: andn a0, a0, a2 +; RV64ZBB-NEXT: and a1, a2, a1 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %mx = and i32 %notmask, 42 + %my = and i32 %mask, %y + %r = or i32 %mx, %my + ret i32 %r +} + +; This is not a canonical form. Testing for completeness only. +define i32 @in_constant_42_vary_invmask(i32 %x, i32 %y, i32 %mask) { +; RV32I-LABEL: in_constant_42_vary_invmask: +; RV32I: # %bb.0: +; RV32I-NEXT: not a0, a2 +; RV32I-NEXT: xori a2, a1, 42 +; RV32I-NEXT: and a0, a2, a0 +; RV32I-NEXT: xor a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: in_constant_42_vary_invmask: +; RV64I: # %bb.0: +; RV64I-NEXT: not a0, a2 +; RV64I-NEXT: xori a2, a1, 42 +; RV64I-NEXT: and a0, a2, a0 +; RV64I-NEXT: xor a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: in_constant_42_vary_invmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: xori a0, a1, 42 +; RV32ZBB-NEXT: andn a0, a0, a2 +; RV32ZBB-NEXT: xor a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: in_constant_42_vary_invmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: xori a0, a1, 42 +; RV64ZBB-NEXT: andn a0, a0, a2 +; RV64ZBB-NEXT: xor a0, a0, a1 +; RV64ZBB-NEXT: ret + %notmask = xor i32 %mask, -1 + %n0 = xor i32 42, %y ; %x + %n1 = and i32 %n0, %notmask + %r = xor i32 %n1, %y + ret i32 %r +} + +; ============================================================================ ; +; Negative tests. Should not be folded. +; ============================================================================ ; + +; Multi-use tests. +declare void @use32(i32) nounwind +define i32 @in_multiuse_A(i32 %x, i32 %y, i32 %z, i32 %mask) nounwind { +; RV32-LABEL: in_multiuse_A: +; RV32: # %bb.0: +; RV32-NEXT: addi sp, sp, -16 +; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32-NEXT: sw s0, 8(sp) # 4-byte Folded Spill +; RV32-NEXT: sw s1, 4(sp) # 4-byte Folded Spill +; RV32-NEXT: mv s0, a1 +; RV32-NEXT: xor a0, a0, a1 +; RV32-NEXT: and s1, a0, a3 +; RV32-NEXT: mv a0, s1 +; RV32-NEXT: call use32@plt +; RV32-NEXT: xor a0, s1, s0 +; RV32-NEXT: lw s1, 4(sp) # 4-byte Folded Reload +; RV32-NEXT: lw s0, 8(sp) # 4-byte Folded Reload +; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32-NEXT: addi sp, sp, 16 +; RV32-NEXT: ret +; +; RV64-LABEL: in_multiuse_A: +; RV64: # %bb.0: +; RV64-NEXT: addi sp, sp, -32 +; RV64-NEXT: sd ra, 24(sp) # 8-byte Folded Spill +; RV64-NEXT: sd s0, 16(sp) # 8-byte Folded Spill +; RV64-NEXT: sd s1, 8(sp) # 8-byte Folded Spill +; RV64-NEXT: mv s0, a1 +; RV64-NEXT: xor a0, a0, a1 +; RV64-NEXT: and s1, a0, a3 +; RV64-NEXT: mv a0, s1 +; RV64-NEXT: call use32@plt +; RV64-NEXT: xor a0, s1, s0 +; RV64-NEXT: ld s1, 8(sp) # 8-byte Folded Reload +; RV64-NEXT: ld s0, 16(sp) # 8-byte Folded Reload +; RV64-NEXT: ld ra, 24(sp) # 8-byte Folded Reload +; RV64-NEXT: addi sp, sp, 32 +; RV64-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + call void @use32(i32 %n1) + %r = xor i32 %n1, %y + ret i32 %r +} + +define i32 @in_multiuse_B(i32 %x, i32 %y, i32 %z, i32 %mask) nounwind { +; RV32-LABEL: in_multiuse_B: +; RV32: # %bb.0: +; RV32-NEXT: addi sp, sp, -16 +; RV32-NEXT: sw ra, 12(sp) # 4-byte Folded Spill +; RV32-NEXT: sw s0, 8(sp) # 4-byte Folded Spill +; RV32-NEXT: sw s1, 4(sp) # 4-byte Folded Spill +; RV32-NEXT: mv s0, a1 +; RV32-NEXT: xor a0, a0, a1 +; RV32-NEXT: and s1, a0, a3 +; RV32-NEXT: call use32@plt +; RV32-NEXT: xor a0, s1, s0 +; RV32-NEXT: lw s1, 4(sp) # 4-byte Folded Reload +; RV32-NEXT: lw s0, 8(sp) # 4-byte Folded Reload +; RV32-NEXT: lw ra, 12(sp) # 4-byte Folded Reload +; RV32-NEXT: addi sp, sp, 16 +; RV32-NEXT: ret +; +; RV64-LABEL: in_multiuse_B: +; RV64: # %bb.0: +; RV64-NEXT: addi sp, sp, -32 +; RV64-NEXT: sd ra, 24(sp) # 8-byte Folded Spill +; RV64-NEXT: sd s0, 16(sp) # 8-byte Folded Spill +; RV64-NEXT: sd s1, 8(sp) # 8-byte Folded Spill +; RV64-NEXT: mv s0, a1 +; RV64-NEXT: xor a0, a0, a1 +; RV64-NEXT: and s1, a0, a3 +; RV64-NEXT: call use32@plt +; RV64-NEXT: xor a0, s1, s0 +; RV64-NEXT: ld s1, 8(sp) # 8-byte Folded Reload +; RV64-NEXT: ld s0, 16(sp) # 8-byte Folded Reload +; RV64-NEXT: ld ra, 24(sp) # 8-byte Folded Reload +; RV64-NEXT: addi sp, sp, 32 +; RV64-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + call void @use32(i32 %n0) + %r = xor i32 %n1, %y + ret i32 %r +} + +; Various bad variants +define i32 @n0_badmask(i32 %x, i32 %y, i32 %mask, i32 %mask2) { +; RV32I-LABEL: n0_badmask: +; RV32I: # %bb.0: +; RV32I-NEXT: and a0, a0, a2 +; RV32I-NEXT: not a2, a3 +; RV32I-NEXT: and a1, a1, a2 +; RV32I-NEXT: or a0, a0, a1 +; RV32I-NEXT: ret +; +; RV64I-LABEL: n0_badmask: +; RV64I: # %bb.0: +; RV64I-NEXT: and a0, a0, a2 +; RV64I-NEXT: not a2, a3 +; RV64I-NEXT: and a1, a1, a2 +; RV64I-NEXT: or a0, a0, a1 +; RV64I-NEXT: ret +; +; RV32ZBB-LABEL: n0_badmask: +; RV32ZBB: # %bb.0: +; RV32ZBB-NEXT: and a0, a0, a2 +; RV32ZBB-NEXT: andn a1, a1, a3 +; RV32ZBB-NEXT: or a0, a0, a1 +; RV32ZBB-NEXT: ret +; +; RV64ZBB-LABEL: n0_badmask: +; RV64ZBB: # %bb.0: +; RV64ZBB-NEXT: and a0, a0, a2 +; RV64ZBB-NEXT: andn a1, a1, a3 +; RV64ZBB-NEXT: or a0, a0, a1 +; RV64ZBB-NEXT: ret + %mx = and i32 %x, %mask + %notmask = xor i32 %mask2, -1 ; %mask2 instead of %mask + %my = and i32 %y, %notmask + %r = or i32 %mx, %my + ret i32 %r +} + +define i32 @n0_badxor(i32 %x, i32 %y, i32 %mask) { +; CHECK-LABEL: n0_badxor: +; CHECK: # %bb.0: +; CHECK-NEXT: and a0, a0, a2 +; CHECK-NEXT: xori a2, a2, 1 +; CHECK-NEXT: and a1, a1, a2 +; CHECK-NEXT: or a0, a0, a1 +; CHECK-NEXT: ret + %mx = and i32 %x, %mask + %notmask = xor i32 %mask, 1 ; instead of -1 + %my = and i32 %y, %notmask + %r = or i32 %mx, %my + ret i32 %r +} + +define i32 @n1_thirdvar(i32 %x, i32 %y, i32 %z, i32 %mask) { +; CHECK-LABEL: n1_thirdvar: +; CHECK: # %bb.0: +; CHECK-NEXT: xor a0, a0, a1 +; CHECK-NEXT: and a0, a0, a3 +; CHECK-NEXT: xor a0, a0, a2 +; CHECK-NEXT: ret + %n0 = xor i32 %x, %y + %n1 = and i32 %n0, %mask + %r = xor i32 %n1, %z ; instead of %y + ret i32 %r +}